Theming
Design tokens, color palette, and semantic aliases — how appearance is defined and overridden.
Token system
All visual decisions are CSS custom properties defined in packages/tokens. Components reference semantic aliases — never raw palette values — so swapping a theme only requires reassigning root variables.
Brand palette
Untitled UI purple. brand-600 is the default action color. Raw scale classes (bg-brand-*) are allowed in components only for mode-independent fills annotated with // dark-ok.
Gray palette
Warm-tinted neutral (zinc-based). Semantic aliases map to this scale and flip between light and dark automatically.
State palettes
Semantic tokens — surfaces
Use these for bg-*. They flip automatically in dark mode — the swatches below reflect the current theme.
--color-bgPage / panel surface--color-bg-secondarySecondary surface--color-bg-tertiaryTertiary / hover surfaceSemantic tokens — text
Use these for text-*. Live samples below show each foreground color on the current surface.
--color-fgPrimary text--color-fg-secondarySecondary text--color-fg-tertiaryTertiary / hint text--color-fg-brandBrand text / icon fill--color-fg-errorError text / icon fill--color-fg-successSuccess text / icon fillSemantic tokens — borders
--color-borderDefault border--color-border-secondarySubtle separator--color-border-brandBrand focus ring--color-border-errorError state border--color-border-successSuccess state borderSemantic tokens — action
Primary fill colors for interactive elements. Use bg-primary, bg-primary-hover, and text-primary-fg on buttons and focus indicators.
Semantic tokens — status
Subtle surface triads for badges and alerts. Each set — bg, border, fg — flips in dark mode (e.g. brand-50 → brand-950).
Forbidden patterns
Never use raw numbered scale values (gray-900, brand-600) on component bg, text, or border utilities. These do not flip under [data-theme="dark"]. Use the semantic aliases above. The pnpm lint:dark check enforces this at CI.