feat(web/m7-362): tokens.json dark/light/flat split + dual-block CSS generator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// Reads ../src/lib/styles/tokens.json and emits tokens.generated.css.
|
||||
// Single source of truth for FabledSword tokens shared with the Flutter
|
||||
// client. Keep output deterministic — Tailwind reads tokens.json directly,
|
||||
// the .css emission is for raw CSS consumers.
|
||||
// the .css emission is for raw CSS consumers and theme switching.
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
@@ -10,19 +10,35 @@ import { dirname, resolve } from 'node:path';
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const tokensPath = resolve(here, '../src/lib/styles/tokens.json');
|
||||
const outPath = resolve(here, '../src/lib/styles/tokens.generated.css');
|
||||
const tokens = JSON.parse(readFileSync(tokensPath, 'utf8'));
|
||||
|
||||
const lines = ['/* GENERATED — do not edit. Source: tokens.json */', ':root {'];
|
||||
for (const [name, value] of Object.entries(tokens.colors)) {
|
||||
lines.push(` --fs-${name}: ${value};`);
|
||||
}
|
||||
for (const [name, value] of Object.entries(tokens.radii)) {
|
||||
lines.push(` --fs-radius-${name}: ${value};`);
|
||||
}
|
||||
lines.push(` --fs-font-display: ${tokens.fontStacks.display};`);
|
||||
lines.push(` --fs-font-body: ${tokens.fontStacks.body};`);
|
||||
lines.push(` --fs-font-mono: ${tokens.fontStacks.mono};`);
|
||||
lines.push('}', '[data-fs-app="minstrel"] {', ` --fs-accent: ${tokens.colors.accent};`, '}', '');
|
||||
export function emit(tokens) {
|
||||
const lines = ['/* GENERATED — do not edit. Source: tokens.json */', ':root {'];
|
||||
for (const [name, value] of Object.entries(tokens.colors.dark)) {
|
||||
lines.push(` --fs-${name}: ${value};`);
|
||||
}
|
||||
for (const [name, value] of Object.entries(tokens.colors.flat)) {
|
||||
lines.push(` --fs-${name}: ${value};`);
|
||||
}
|
||||
for (const [name, value] of Object.entries(tokens.radii)) {
|
||||
lines.push(` --fs-radius-${name}: ${value};`);
|
||||
}
|
||||
lines.push(` --fs-font-display: ${tokens.fontStacks.display};`);
|
||||
lines.push(` --fs-font-body: ${tokens.fontStacks.body};`);
|
||||
lines.push(` --fs-font-mono: ${tokens.fontStacks.mono};`);
|
||||
lines.push('}');
|
||||
|
||||
writeFileSync(outPath, lines.join('\n'));
|
||||
console.log(`wrote ${outPath}`);
|
||||
lines.push('[data-theme="light"] {');
|
||||
for (const [name, value] of Object.entries(tokens.colors.light)) {
|
||||
lines.push(` --fs-${name}: ${value};`);
|
||||
}
|
||||
lines.push('}');
|
||||
|
||||
lines.push('[data-fs-app="minstrel"] {', ` --fs-accent: ${tokens.colors.flat.accent};`, '}', '');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const tokens = JSON.parse(readFileSync(tokensPath, 'utf8'));
|
||||
writeFileSync(outPath, emit(tokens));
|
||||
console.log(`wrote ${outPath}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user