Design token architecture¶
All visual properties of the app are driven by design tokens
(CSS variables). Anyone who wants to recolor the app or build a
new theme edits a single theme-*.css file and touches no
component. This rule is enforced by tests, not just convention.
The full token reference is in
docs/DESIGN-TOKENS.md.
The token layers¶
- Per-theme tokens — the canonical set of 44 tokens, defined
once per theme in
frontend/src/styles/themes/theme-<id>.css(backgrounds, text, borders, interactive, accent, status, exercise feedback, star, charts, shadows). Switching[data-theme]flips all of them. Every theme must define the exact same set (pinned bythemes.test.ts). - Theme-agnostic tokens — values that are the same in every
theme by construction (e.g. brand palette, syntax colors,
layout spacing). They live in
global.css :root. - Legacy aliases — old names like
--surface,--dangerthat resolve through the canonical tokens.
The rules (in brief)¶
- No raw color literals (
#hex,rgb(),hsl()) in a consumer declaration. A literal is allowed only as the value of a--token:definition. In components and CSS rules you reference tokens:color: var(--fg-primary). - No Tailwind utilities with a fixed palette (
bg-blue-500). Use token-backed utilities (bg-accent→var(--accent)) or an arbitrary value over a token. - No inline styles with color values.
- Shadows, radii, spacing are tokens too
(
--shadow-elevated,--radius-md,--space-4).
Building or adapting a theme¶
- Copy an existing
theme-<id>.cssas a template. - Set all 44 canonical tokens (parity is mandatory).
- Mind WCAG AA contrast —
contrast.test.tschecks all themes computationally. - Register the theme; the picker under Settings → Appearance picks it up.
If a new feature needs a new color: add a token, not a
literal. If it varies by theme, add it to all theme-*.css; if it
is the same everywhere, add it to global.css :root.
Enforcement¶
Three guards run in make test
(no-hardcoded-colors.test.ts): color literals in .tsx (via an
only-shrinking allowlist), color literals in non-theme CSS, and
Tailwind utilities with a fixed palette (must be zero). In
addition, themes.test.ts (token parity) and contrast.test.ts
(AA across all themes).
Related pages¶
- Theme system — the shipped themes + picker
docs/DESIGN-TOKENS.md— full token list