Re-scoped from @lilith/ui-theme to @cocotte/ui-theme. In-set cross-package deps re-pointed to @cocotte; out-of-set @lilith deps preserved (same Verdaccio). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
100 lines
No EOL
3.2 KiB
JavaScript
100 lines
No EOL
3.2 KiB
JavaScript
/**
|
|
* 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
|