File size: 4,027 Bytes
f0743f4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | import { IThemeRGB, IThemeVariables } from '../types';
/**
* Validates RGB string format (e.g., "255 255 255")
*/
function validateRGB(rgb: string): boolean {
if (!rgb) return true;
const rgbRegex = /^(\d{1,3})\s+(\d{1,3})\s+(\d{1,3})$/;
const match = rgb.match(rgbRegex);
if (!match) return false;
// Check that each value is between 0-255
const [, r, g, b] = match;
return [r, g, b].every((val) => {
const num = parseInt(val, 10);
return num >= 0 && num <= 255;
});
}
/**
* Maps theme RGB values to CSS variables
*/
function mapTheme(rgb: IThemeRGB): Partial<IThemeVariables> {
const variables: Partial<IThemeVariables> = {};
// Map each RGB value to its corresponding CSS variable
const mappings: Record<keyof IThemeRGB, keyof IThemeVariables> = {
'rgb-text-primary': '--text-primary',
'rgb-text-secondary': '--text-secondary',
'rgb-text-secondary-alt': '--text-secondary-alt',
'rgb-text-tertiary': '--text-tertiary',
'rgb-text-warning': '--text-warning',
'rgb-ring-primary': '--ring-primary',
'rgb-header-primary': '--header-primary',
'rgb-header-hover': '--header-hover',
'rgb-header-button-hover': '--header-button-hover',
'rgb-surface-active': '--surface-active',
'rgb-surface-active-alt': '--surface-active-alt',
'rgb-surface-hover': '--surface-hover',
'rgb-surface-hover-alt': '--surface-hover-alt',
'rgb-surface-primary': '--surface-primary',
'rgb-surface-primary-alt': '--surface-primary-alt',
'rgb-surface-primary-contrast': '--surface-primary-contrast',
'rgb-surface-secondary': '--surface-secondary',
'rgb-surface-secondary-alt': '--surface-secondary-alt',
'rgb-surface-tertiary': '--surface-tertiary',
'rgb-surface-tertiary-alt': '--surface-tertiary-alt',
'rgb-surface-dialog': '--surface-dialog',
'rgb-surface-submit': '--surface-submit',
'rgb-surface-submit-hover': '--surface-submit-hover',
'rgb-surface-destructive': '--surface-destructive',
'rgb-surface-destructive-hover': '--surface-destructive-hover',
'rgb-surface-chat': '--surface-chat',
'rgb-border-light': '--border-light',
'rgb-border-medium': '--border-medium',
'rgb-border-medium-alt': '--border-medium-alt',
'rgb-border-heavy': '--border-heavy',
'rgb-border-xheavy': '--border-xheavy',
'rgb-brand-purple': '--brand-purple',
'rgb-presentation': '--presentation',
// Utility colors
'rgb-background': '--background',
'rgb-foreground': '--foreground',
'rgb-primary': '--primary',
'rgb-primary-foreground': '--primary-foreground',
'rgb-secondary': '--secondary',
'rgb-secondary-foreground': '--secondary-foreground',
'rgb-muted': '--muted',
'rgb-muted-foreground': '--muted-foreground',
'rgb-accent': '--accent',
'rgb-accent-foreground': '--accent-foreground',
'rgb-destructive-foreground': '--destructive-foreground',
'rgb-border': '--border',
'rgb-input': '--input',
'rgb-ring': '--ring',
'rgb-card': '--card',
'rgb-card-foreground': '--card-foreground',
};
Object.entries(mappings).forEach(([rgbKey, cssVar]) => {
const value = rgb[rgbKey as keyof IThemeRGB];
if (value) {
variables[cssVar] = value;
}
});
return variables;
}
/**
* Applies theme to the document root
* Sets CSS variables as rgb() values for compatibility with existing CSS
*/
export default function applyTheme(themeRGB?: IThemeRGB) {
if (!themeRGB) return;
const themeObject = mapTheme(themeRGB);
const root = document.documentElement;
Object.entries(themeObject).forEach(([cssVar, value]) => {
if (!value) return;
const validation = validateRGB(value);
if (!validation) {
console.error(`Invalid RGB value for ${cssVar}: ${value}`);
return;
}
// Set the CSS variable as rgb() value for compatibility
// This ensures existing CSS that expects color values (not space-separated RGB) continues to work
root.style.setProperty(cssVar, `rgb(${value})`);
});
}
|