The raw accent fails AA as text on every dark surface, not only on its
own tint: 3.04:1 on the page, 2.70 on iron, 2.21 on slate, against 4.5.
accent-fg (the house formula, 45% toward parchment) measures 5.62 at
worst across both modes. The operator chose the readable colour over the
signature teal for text, on 2026-10-08.
- 36 sites swap. They are 35 Tailwind uses: links, "Now playing", the
ingest progress line, active shuffle/repeat, the liked heart, the app
download icon and its hover. The last is the alphabet rail's pending
spinner in CSS. Icons follow the text: as graphics they need only
3:1, and the raw accent misses even that on iron.
- check-tint-contrast adds accent to TEXT_NEVER_RAW, so a new raw
text-accent or color: var(--fs-accent) fails the web lane. Run against
the files before the swap, it finds all 36. Borders, rings and fills
keep the raw accent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Raw error red fails AA as text even with no tint behind it: in dark mode
it measures 3.63:1 on obsidian, 3.23 on iron and 2.64 on slate, against
4.5. error-fg (the house formula, 50% toward parchment) measures 5.30 at
worst across both modes.
- All 19 text-error uses become text-error-fg: the "Couldn't load"
messages on the admin pages, the integrations form errors, the flag
popover, and the error toast's text. The toast keeps its error border,
since a border is a graphic with a 3:1 floor.
- check-tint-contrast flags raw error text anywhere (text-error,
class:text-error, color: var(--fs-error)) and leaves borders and
outlines alone. Run against the files before the swap, it finds all 19.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
The Flutter client (#356) needs to consume the same FabledSword tokens
the web SPA uses. Move the data into a structured tokens.json file and
generate tokens.generated.css from it on every Vite build. The Flutter
side gets its own generator pointed at the same JSON.
No visual change — generated CSS matches the previous hand-written file.
Also installs @types/node devDependency: tsconfig already declared
"types": ["node"] but no Node builtins were referenced in TS until now,
so the missing types were latent. The new vite.config.ts plugin imports
node:child_process, surfacing the gap.