Add sub-skills from ui-ux-pro-max-skill repo
Added: - banner-design (new) - brand (new) - design-system (new) - slides (new) - ui-ux-pro-max data/scripts (from GitHub clone) - ui-styling data/scripts (from GitHub clone)
This commit is contained in:
205
skills/website-creator/design-system/scripts/generate-tokens.cjs
Normal file
205
skills/website-creator/design-system/scripts/generate-tokens.cjs
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Generate CSS variables from design tokens JSON
|
||||
*
|
||||
* Usage:
|
||||
* node generate-tokens.cjs --config tokens.json -o tokens.css
|
||||
* node generate-tokens.cjs --config tokens.json --format tailwind
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* Parse command line arguments
|
||||
*/
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
const options = {
|
||||
config: null,
|
||||
output: null,
|
||||
format: 'css' // css | tailwind
|
||||
};
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === '--config' || args[i] === '-c') {
|
||||
options.config = args[++i];
|
||||
} else if (args[i] === '--output' || args[i] === '-o') {
|
||||
options.output = args[++i];
|
||||
} else if (args[i] === '--format' || args[i] === '-f') {
|
||||
options.format = args[++i];
|
||||
} else if (args[i] === '--help' || args[i] === '-h') {
|
||||
console.log(`
|
||||
Usage: node generate-tokens.cjs [options]
|
||||
|
||||
Options:
|
||||
-c, --config <file> Input JSON token file (required)
|
||||
-o, --output <file> Output file (default: stdout)
|
||||
-f, --format <type> Output format: css | tailwind (default: css)
|
||||
-h, --help Show this help
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve token references like {primitive.color.blue.600}
|
||||
*/
|
||||
function resolveReference(value, tokens) {
|
||||
if (typeof value !== 'string' || !value.startsWith('{')) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const path = value.slice(1, -1).split('.');
|
||||
let result = tokens;
|
||||
|
||||
for (const key of path) {
|
||||
result = result?.[key];
|
||||
}
|
||||
|
||||
if (result?.$value) {
|
||||
return resolveReference(result.$value, tokens);
|
||||
}
|
||||
|
||||
return result || value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert token name to CSS variable name
|
||||
*/
|
||||
function toCssVarName(path) {
|
||||
return '--' + path.join('-').replace(/\./g, '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Flatten tokens into CSS variables
|
||||
*/
|
||||
function flattenTokens(obj, tokens, prefix = [], result = {}) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const currentPath = [...prefix, key];
|
||||
|
||||
if (value && typeof value === 'object') {
|
||||
if (value.$value !== undefined) {
|
||||
// This is a token
|
||||
const cssVar = toCssVarName(currentPath);
|
||||
const resolvedValue = resolveReference(value.$value, tokens);
|
||||
result[cssVar] = resolvedValue;
|
||||
} else {
|
||||
// Recurse into nested object
|
||||
flattenTokens(value, tokens, currentPath, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CSS output
|
||||
*/
|
||||
function generateCSS(tokens) {
|
||||
const primitive = flattenTokens(tokens.primitive || {}, tokens, ['primitive']);
|
||||
const semantic = flattenTokens(tokens.semantic || {}, tokens, []);
|
||||
const component = flattenTokens(tokens.component || {}, tokens, []);
|
||||
const darkSemantic = flattenTokens(tokens.dark?.semantic || {}, tokens, []);
|
||||
|
||||
let css = `/* Design Tokens - Auto-generated */
|
||||
/* Do not edit directly - modify tokens.json instead */
|
||||
|
||||
/* === PRIMITIVES === */
|
||||
:root {
|
||||
${Object.entries(primitive).map(([k, v]) => ` ${k}: ${v};`).join('\n')}
|
||||
}
|
||||
|
||||
/* === SEMANTIC === */
|
||||
:root {
|
||||
${Object.entries(semantic).map(([k, v]) => ` ${k}: ${v};`).join('\n')}
|
||||
}
|
||||
|
||||
/* === COMPONENTS === */
|
||||
:root {
|
||||
${Object.entries(component).map(([k, v]) => ` ${k}: ${v};`).join('\n')}
|
||||
}
|
||||
`;
|
||||
|
||||
if (Object.keys(darkSemantic).length > 0) {
|
||||
css += `
|
||||
/* === DARK MODE === */
|
||||
.dark {
|
||||
${Object.entries(darkSemantic).map(([k, v]) => ` ${k}: ${v};`).join('\n')}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Tailwind config output
|
||||
*/
|
||||
function generateTailwind(tokens) {
|
||||
const semantic = flattenTokens(tokens.semantic || {}, tokens, []);
|
||||
|
||||
// Extract colors for Tailwind
|
||||
const colors = {};
|
||||
for (const [key, value] of Object.entries(semantic)) {
|
||||
if (key.includes('color')) {
|
||||
const name = key.replace('--color-', '').replace(/-/g, '.');
|
||||
colors[name] = `var(${key})`;
|
||||
}
|
||||
}
|
||||
|
||||
return `// Tailwind color config - Auto-generated
|
||||
// Add to tailwind.config.ts theme.extend.colors
|
||||
|
||||
module.exports = {
|
||||
colors: ${JSON.stringify(colors, null, 2).replace(/"/g, "'")}
|
||||
};
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
function main() {
|
||||
const options = parseArgs();
|
||||
|
||||
if (!options.config) {
|
||||
console.error('Error: --config is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Resolve config path
|
||||
const configPath = path.resolve(process.cwd(), options.config);
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
console.error(`Error: Config file not found: ${configPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read and parse tokens
|
||||
const tokens = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
||||
|
||||
// Generate output
|
||||
let output;
|
||||
if (options.format === 'tailwind') {
|
||||
output = generateTailwind(tokens);
|
||||
} else {
|
||||
output = generateCSS(tokens);
|
||||
}
|
||||
|
||||
// Write output
|
||||
if (options.output) {
|
||||
const outputPath = path.resolve(process.cwd(), options.output);
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, output);
|
||||
console.log(`Generated: ${outputPath}`);
|
||||
} else {
|
||||
console.log(output);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user