Files
minstrel/web/scripts/tokens-to-css.js
T
bvandeusenandClaude Opus 5.5 fdee77eaec
release / web (push) Successful in 1m22s
release / govulncheck (push) Successful in 38s
release / go (push) Successful in 1m48s
release / integration (push) Successful in 5m1s
release / android (push) Successful in 5m41s
release / Build signed APK (releases and dev) (push) Successful in 5m11s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 1m17s
release / Verify release artifacts (tag releases only) (push) Skipped
fix(web): text on a tint of its own hue uses the house -fg tokens (#3150)
A hue painted as text on a color-mix tint of itself sits close to the
surface under it. On Minstrel's surfaces the raw accent on its 15% tint
measures 1.97:1 at worst (dark mode, hover surface), against AA's 4.5.

- tokens.json gains colors.fg: the five FabledSword -fg formulas (accent
  45%, success 45%, warning, error and info 50%), each mixed toward
  parchment so one declaration serves both modes. Success is Minstrel's
  moss. tokens-to-css emits them in :root.
- Tailwind exposes them as text-accent-fg, text-warning-fg, text-error-fg
  and text-info-fg.
- 23 sites swapped: 14 Tailwind class strings (PlayerBar and the admin
  count pills) and 9 CSS rules (StatusPill's four tones and five accent
  chips). Worst case after: accent-fg 5.03, error-fg 4.75, warning-fg
  4.92, success-fg 4.85.
- scripts/check-tint-contrast.js finds the pair in either spelling. Its
  test scans src in the web Vitest lane and fails on any new site, with
  fixture cases showing it can fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:51:52 -04:00

53 lines
2.3 KiB
JavaScript

#!/usr/bin/env node
// 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 and theme switching.
import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
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');
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};`);
}
// The house -fg tokens (FabledSword, 2026-08-27; #3150): a hue as TEXT on a
// tint of itself, mixed toward parchment. Parchment inverts in light mode,
// so one declaration lightens the text on dark surfaces and darkens it on
// light ones. Declared on :root, the element data-theme is set on, so the
// var()s resolve against that mode's parchment.
for (const [name, value] of Object.entries(tokens.colors.fg ?? {})) {
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('}');
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}`);
}