191 lines
5.8 KiB
JavaScript
191 lines
5.8 KiB
JavaScript
import StyleDictionary from 'style-dictionary';
|
|
|
|
// Custom format: CSS custom properties with RGB triplets for Tailwind v4 compatibility
|
|
// Outputs `--background: 240 240 236;` format that existing components expect
|
|
StyleDictionary.registerFormat({
|
|
name: 'css/rgb-variables',
|
|
format: ({ dictionary, options }) => {
|
|
const selector = options.selector || ':root';
|
|
const header = options.header || '';
|
|
|
|
function hexToRgb(hex) {
|
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
if (!result) return null;
|
|
return `${parseInt(result[1], 16)} ${parseInt(result[2], 16)} ${parseInt(result[3], 16)}`;
|
|
}
|
|
|
|
// Map semantic token paths to CSS variable names
|
|
function getCssVarName(token) {
|
|
const path = token.path;
|
|
// color.semantic.X or color.dark.X → --X
|
|
if (path[0] === 'color' && (path[1] === 'semantic' || path[1] === 'dark')) {
|
|
const rest = path.slice(2);
|
|
// Handle nested like chart.1, sidebar.background
|
|
if (rest[0] === 'chart') return `--chart-${rest[1]}`;
|
|
if (rest[0] === 'sidebar') {
|
|
const subParts = rest.slice(1);
|
|
if (subParts.length === 1 && subParts[0] === 'background') return '--sidebar';
|
|
return `--sidebar-${subParts.join('-')}`;
|
|
}
|
|
return `--${rest.join('-')}`;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const lines = [];
|
|
dictionary.allTokens.forEach((token) => {
|
|
const varName = getCssVarName(token);
|
|
if (!varName) return;
|
|
|
|
const value = token.value || token.$value;
|
|
const rgb = hexToRgb(value);
|
|
if (rgb) {
|
|
const desc = token.$description || token.description;
|
|
if (desc) lines.push(` /* ${desc} */`);
|
|
lines.push(` ${varName}: ${rgb};`);
|
|
}
|
|
});
|
|
|
|
return `${header}\n${selector} {\n${lines.join('\n')}\n}`;
|
|
},
|
|
});
|
|
|
|
// Custom format: TypeScript constants
|
|
StyleDictionary.registerFormat({
|
|
name: 'typescript/constants',
|
|
format: ({ dictionary }) => {
|
|
const lines = [
|
|
'// Auto-generated by Style Dictionary — DO NOT EDIT',
|
|
'// Source: tokens/*.json (W3C DTCG format)',
|
|
'',
|
|
];
|
|
|
|
// Group tokens by top-level category
|
|
const groups = {};
|
|
dictionary.allTokens.forEach((token) => {
|
|
const group = token.path[0];
|
|
if (!groups[group]) groups[group] = [];
|
|
groups[group].push(token);
|
|
});
|
|
|
|
for (const [group, tokens] of Object.entries(groups)) {
|
|
const constName = group.charAt(0).toUpperCase() + group.slice(1) + 'Tokens';
|
|
lines.push(`export const ${constName} = {`);
|
|
tokens.forEach((token) => {
|
|
const key = token.path.slice(1).join('.');
|
|
const value = token.value || token.$value;
|
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
lines.push(` '${key}': '${value}',`);
|
|
}
|
|
});
|
|
lines.push('} as const;');
|
|
lines.push('');
|
|
}
|
|
|
|
return lines.join('\n');
|
|
},
|
|
});
|
|
|
|
// Custom format: Markdown reference
|
|
StyleDictionary.registerFormat({
|
|
name: 'markdown/reference',
|
|
format: ({ dictionary }) => {
|
|
const lines = [
|
|
'# Greyhaven Design Tokens Reference',
|
|
'',
|
|
'> Auto-generated by Style Dictionary — DO NOT EDIT',
|
|
'> Source: `tokens/*.json` (W3C DTCG format)',
|
|
'',
|
|
];
|
|
|
|
const groups = {};
|
|
dictionary.allTokens.forEach((token) => {
|
|
const group = token.path[0];
|
|
if (!groups[group]) groups[group] = [];
|
|
groups[group].push(token);
|
|
});
|
|
|
|
for (const [group, tokens] of Object.entries(groups)) {
|
|
lines.push(`## ${group.charAt(0).toUpperCase() + group.slice(1)}`);
|
|
lines.push('');
|
|
lines.push('| Token | Value | Description |');
|
|
lines.push('|-------|-------|-------------|');
|
|
tokens.forEach((token) => {
|
|
const name = token.path.join('.');
|
|
const value = token.value || token.$value;
|
|
const desc = token.$description || token.description || '';
|
|
const displayValue = typeof value === 'object' ? JSON.stringify(value) : value;
|
|
lines.push(`| \`${name}\` | \`${displayValue}\` | ${desc} |`);
|
|
});
|
|
lines.push('');
|
|
}
|
|
|
|
return lines.join('\n');
|
|
},
|
|
});
|
|
|
|
export default {
|
|
source: ['tokens/**/*.json'],
|
|
preprocessors: ['tokens-studio'],
|
|
platforms: {
|
|
// CSS custom properties for light theme (semantic tokens)
|
|
cssLight: {
|
|
transformGroup: 'css',
|
|
buildPath: 'app/tokens/',
|
|
files: [
|
|
{
|
|
destination: 'tokens-light.css',
|
|
format: 'css/rgb-variables',
|
|
filter: (token) => {
|
|
return token.path[0] === 'color' && token.path[1] === 'semantic';
|
|
},
|
|
options: {
|
|
selector: ':root',
|
|
header: '/* Greyhaven Design Tokens — Light Theme\n Auto-generated by Style Dictionary — DO NOT EDIT\n Source: tokens/color.json */\n',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
// CSS custom properties for dark theme
|
|
cssDark: {
|
|
transformGroup: 'css',
|
|
buildPath: 'app/tokens/',
|
|
files: [
|
|
{
|
|
destination: 'tokens-dark.css',
|
|
format: 'css/rgb-variables',
|
|
filter: (token) => {
|
|
return token.path[0] === 'color' && token.path[1] === 'dark';
|
|
},
|
|
options: {
|
|
selector: '.dark',
|
|
header: '/* Greyhaven Design Tokens — Dark Theme\n Auto-generated by Style Dictionary — DO NOT EDIT\n Source: tokens/color.json */\n',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
// TypeScript constants
|
|
ts: {
|
|
transformGroup: 'js',
|
|
buildPath: 'app/tokens/',
|
|
files: [
|
|
{
|
|
destination: 'tokens.ts',
|
|
format: 'typescript/constants',
|
|
},
|
|
],
|
|
},
|
|
// Markdown reference
|
|
docs: {
|
|
transformGroup: 'css',
|
|
buildPath: 'app/tokens/',
|
|
files: [
|
|
{
|
|
destination: 'TOKENS.md',
|
|
format: 'markdown/reference',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|