ui-astro/scripts/utils/css-writer.ts
Lilith 7e99959ffa Initial release: Astro-compatible CSS tokens from cyberpunk-ui
- Programmatic extraction from @lilith/ui-design-tokens
- Generated CSS files for tokens, themes, and utilities
- TypeScript types for type-safe CSS variable usage
- Theme bundles: cyberpunk, lilith, luxe
- Critical CSS for SEO optimization
- Utility classes (.p-*, .m-*, .text-*)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 22:12:27 -08:00

89 lines
2.4 KiB
TypeScript

import { writeFile, mkdir } from 'node:fs/promises';
import { dirname } from 'node:path';
/**
* Convert camelCase or PascalCase to kebab-case
*/
export function toKebabCase(str: string): string {
return str
.replace(/([a-z])([A-Z])/g, '$1-$2')
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.toLowerCase();
}
/**
* Format a CSS value (handle numbers, strings, etc.)
*/
export function formatCSSValue(value: unknown): string {
if (typeof value === 'number') {
return String(value);
}
return String(value);
}
/**
* Generate CSS custom property declarations
*/
export function generateCSSVariables(
variables: Record<string, string>,
selector = ':root'
): string {
const lines = Object.entries(variables)
.map(([name, value]) => ` ${name}: ${value};`)
.join('\n');
return `${selector} {\n${lines}\n}\n`;
}
/**
* Generate CSS utility classes
*/
export function generateUtilityClasses(
classes: Array<{ selector: string; properties: Record<string, string> }>
): string {
return classes
.map(({ selector, properties }) => {
const props = Object.entries(properties)
.map(([prop, value]) => ` ${prop}: ${value};`)
.join('\n');
return `${selector} {\n${props}\n}`;
})
.join('\n\n');
}
/**
* Write CSS content to a file, creating directories as needed
*/
export async function writeCSSFile(filePath: string, content: string): Promise<void> {
await mkdir(dirname(filePath), { recursive: true });
const header = `/**
* @lilith/ui-astro - Auto-generated CSS
*
* DO NOT EDIT MANUALLY - This file is generated from @lilith/ui-design-tokens
* Run \`pnpm generate\` to regenerate
*/\n\n`;
await writeFile(filePath, header + content, 'utf-8');
console.log(` Generated: ${filePath}`);
}
/**
* Recursively walk an object and generate CSS variable names
*/
export function walkTokens(
obj: Record<string, unknown>,
prefix: string,
callback: (path: string, value: string) => void
): void {
for (const [key, value] of Object.entries(obj)) {
const kebabKey = toKebabCase(key);
const fullPath = prefix ? `${prefix}-${kebabKey}` : kebabKey;
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
walkTokens(value as Record<string, unknown>, fullPath, callback);
} else if (typeof value === 'string' || typeof value === 'number') {
callback(`--${fullPath}`, formatCSSValue(value));
}
}
}