Product primaries
Each product defines its own primary in its DESIGN.md. Draw it from a Radix colour scale, so states, tints, and dark-mode variants are already defined. A surface that lives inside a host product uses the host's primary rather than carrying its own.
What stays shared across all products: neutrals (Radix gray/slate), functional colours (success/warning/danger from Radix grass/amber/red, COL-2), typography, illustration style, and spacing. A product changes its accent, never its character.
The Radix scales
Each Radix colour ships as a 12-step scale with defined roles: backgrounds, component states, borders, solid fills, and text. Picking from Radix means every product's hover, pressed, and disabled states are consistent by construction.
Shared neutrals
Neutrals stay shared across every product: gray for general UI, slate where a cooler tone reads better.
Functional colours
Functional colours always come from the shared Radix scales: grass for success, amber for warning, and red for danger. Never pick one ad hoc (COL-2).
The warning text token is darkened past Radix amber-11 on purpose. Amber-11 caps at roughly 4.6:1 even on white, so it fails AA on the tinted warning background. The darkened value clears 6.1:1 instead.
Semantic tokens
Tokens describe purpose, not appearance, so a colour can change without touching a single component.
| Token | Role | |
|---|---|---|
--background | The page canvas. | |
--surface | Panels and cards sitting on the page. | |
--foreground | Primary text ink. | |
--muted-foreground | Secondary text — darkened from zinc-500 to clear AA on the muted fill. | |
--border | Hairline dividers and default component borders. | |
--muted | Active fill and code background. | |
--accent | Hover fill. | |
--border-strong | Hover border. | |
--prose-body | Body text ink inside long-form content. | |
--ring | Focus ring colour. |
| Token | Role | |
|---|---|---|
--success | Success text — Radix grass-11. | |
--success-9 | Success solid fill — Radix grass-9. | |
--success-subtle | Success badge background. | |
--success-muted | Success badge border. | |
--warning | Warning text — darkened past Radix amber-11 to clear AA on a tinted background. | |
--warning-9 | Warning solid fill — Radix amber-9. | |
--warning-subtle | Warning badge background. | |
--warning-muted | Warning badge border. | |
--danger | Danger text — Radix red-11. | |
--danger-9 | Danger solid fill — Radix red-9. | |
--danger-subtle | Danger badge background. | |
--danger-muted | Danger badge border. |
Usage
// Semantic Tailwind classes — the default
<div className="bg-surface text-foreground border border-border" />
<span className="text-success bg-success-subtle" />
// CSS-variable escape hatch, when no class fits
<div style={{ background: "var(--muted)" }} />Best practices
- DoUse the product's own primary for primary actions and brand moments (COL-1).
- DoTake success, warning, and danger colours from the shared Radix scales (COL-2).
- DoPair colour with a text label — never signal with colour alone.
- Don'tHardcode a hex value in a component; use a semantic token instead (TOK-1).
- Don'tUse purple or violet gradients, cyan-on-dark, glow accents, or gradient text (SLP-1, SLP-2).