/** * CSS Custom Properties Generator * * Converts a ThemeInterface into a flat map of CSS custom properties. * Enables theme values to be consumed via var(--theme-*) in plain CSS, * CSS modules, or non-styled-components contexts. */ /** * Section aliases map ThemeInterface top-level keys to cleaner CSS var prefixes. * Keys not listed here use the default kebab-case conversion. */ const SECTION_ALIASES = { colors: 'color', shadows: 'shadow', borderRadius: 'radius', transitions: 'transition', typography: 'type', spacing: 'spacing', zIndex: 'z', duration: 'duration', easing: 'easing', opacity: 'opacity', letterSpacing: 'letter-spacing', borderWidth: 'border-width', }; /** * Keys to skip during recursive traversal. * - extensions: theme-specific, not suitable for generic CSS vars * - breakpoints: typically used in media queries, not as var values */ const SKIP_KEYS = new Set(['extensions', 'breakpoints']); /** * Convert a camelCase or PascalCase string to kebab-case. */ function toKebab(str) { return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); } /** * Recursively flatten an object into CSS variable entries. */ function flatten(obj, prefix, result) { for (const [key, value] of Object.entries(obj)) { if (value === undefined || value === null) continue; if (typeof value === 'function') continue; if (SKIP_KEYS.has(key)) continue; const kebabKey = toKebab(key); const varName = `${prefix}-${kebabKey}`; if (typeof value === 'string') { result[varName] = value; } else if (typeof value === 'number') { result[varName] = String(value); } else if (typeof value === 'object' && !Array.isArray(value)) { flatten(value, varName, result); } } } /** * Flatten a ThemeInterface into a Record of CSS custom property names to values. * * @param theme - The theme adapter to flatten * @param prefix - CSS var prefix (default: 'theme') * @returns Record mapping `--{prefix}-{section}-{path}` to string values * * @example * ```typescript * const vars = flattenThemeToCssVars(cyberpunkAdapter) * // vars['--theme-color-primary-main'] === '#ff00ff' * // vars['--theme-spacing-md'] === '1rem' * // vars['--theme-type-font-family-heading'] === '"Courier New", ...' * ``` */ export function flattenThemeToCssVars(theme, prefix = 'theme') { const result = {}; for (const [section, value] of Object.entries(theme)) { if (value === undefined || value === null) continue; if (typeof value === 'function') continue; if (SKIP_KEYS.has(section)) continue; const sectionPrefix = SECTION_ALIASES[section] ?? toKebab(section); const varPrefix = `--${prefix}-${sectionPrefix}`; if (typeof value === 'string') { result[varPrefix] = value; } else if (typeof value === 'number') { result[varPrefix] = String(value); } else if (typeof value === 'object' && !Array.isArray(value)) { flatten(value, varPrefix, result); } } return result; } //# sourceMappingURL=css-variables.js.map