/** * CSS Variables Generator * Converts design tokens to CSS custom properties * * @module @kodexalabs/design-tokens/css */ import { designTokens, darkThemeTokens, type DesignTokens } from './tokens'; /** * Converts a nested object to CSS custom properties */ function objectToCSSVars( obj: Record, prefix: string = '', separator: string = '-' ): Record { const vars: Record = {}; for (const [key, value] of Object.entries(obj)) { const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase(); const fullKey = prefix ? `${prefix}${separator}${cssKey}` : cssKey; if (value && typeof value === 'object' && !Array.isArray(value)) { // Handle ColorToken objects if ('hsl' in value && typeof value.hsl === 'string') { vars[fullKey] = value.hsl; } else { // Recursively process nested objects Object.assign(vars, objectToCSSVars(value as Record, fullKey, separator)); } } else if (typeof value === 'string' || typeof value === 'number') { vars[fullKey] = String(value); } } return vars; } /** * Generates CSS custom properties string from design tokens * * @param tokens - Design tokens object * @param prefix - Prefix for CSS variables (default: '--') * @param selector - CSS selector to wrap variables (default: ':root') * @returns CSS string with custom properties * * @example * ```typescript * const css = generateCSSVars(designTokens); * // Returns: ':root { --color-brand-magenta: hsl(336 98% 52%); ... }' * ``` */ export function generateCSSVars( tokens: DesignTokens | Partial, prefix: string = '--', selector: string = ':root' ): string { const vars = objectToCSSVars(tokens as Record); const cssLines = Object.entries(vars).map( ([key, value]) => ` ${prefix}${key}: ${value};` ); return `${selector} {\n${cssLines.join('\n')}\n}`; } /** * Generates CSS custom properties for light theme * * @example * ```typescript * const lightCSS = generateLightThemeCSS(); * ``` */ export function generateLightThemeCSS(): string { return generateCSSVars(designTokens, '--', ':root'); } /** * Generates CSS custom properties for dark theme * * @example * ```typescript * const darkCSS = generateDarkThemeCSS(); * ``` */ export function generateDarkThemeCSS(): string { return generateCSSVars(darkThemeTokens, '--', ':root[data-theme="dark"], :root.dark'); } /** * Generates complete CSS with both light and dark themes * * @example * ```typescript * const css = generateCompleteCSS(); * // Inject into document or save to file * ``` */ export function generateCompleteCSS(): string { return `${generateLightThemeCSS()}\n\n${generateDarkThemeCSS()}`; } /** * Applies CSS variables to document at runtime * Useful for client-side theme switching * * @param theme - Theme to apply ('light' or 'dark') * * @example * ```typescript * // In your React app * useEffect(() => { * applyCSSVarsToDocument('dark'); * }, []); * ``` */ export function applyCSSVarsToDocument(theme: 'light' | 'dark' = 'light'): void { if (typeof document === 'undefined') return; const tokens = theme === 'dark' ? darkThemeTokens : designTokens; const vars = objectToCSSVars(tokens as Record); // Apply to document root const root = document.documentElement; for (const [key, value] of Object.entries(vars)) { root.style.setProperty(`--${key}`, value); } // Set data attribute for theme root.setAttribute('data-theme', theme); if (theme === 'dark') { root.classList.add('dark'); } else { root.classList.remove('dark'); } } /** * Gets a CSS variable value from the document * * @param varName - CSS variable name (without -- prefix) * @returns CSS variable value or empty string if not found * * @example * ```typescript * const primaryColor = getCSSVar('color-brand-magenta'); * ``` */ export function getCSSVar(varName: string): string { if (typeof document === 'undefined') return ''; return getComputedStyle(document.documentElement) .getPropertyValue(`--${varName}`) .trim(); } /** * Sets a CSS variable value on the document * * @param varName - CSS variable name (without -- prefix) * @param value - CSS variable value * * @example * ```typescript * setCSSVar('color-brand-magenta', 'hsl(336 98% 60%)'); * ``` */ export function setCSSVar(varName: string, value: string): void { if (typeof document === 'undefined') return; document.documentElement.style.setProperty(`--${varName}`, value); } /** * Export CSS as a string for injection into