Color
Two tiers: fixed primitive ramps (the raw palette) and intent-based semantic roles that components reference. Theme by overriding the semantic layer — primitives never move.
Brand color
The active theme's brand ramp — these semantic tokens update live when you switch themes.
Primitive ramps
Raw values. Never used directly in components — they exist so semantic roles can point at them.
Step usage
Each ramp follows the Radix scale convention. Quick reference:
| Steps | Purpose |
|---|---|
| 1–2 | App and component backgrounds |
| 3–5 | Interactive component backgrounds (hover, pressed) |
| 6–8 | Borders and separators |
| 9 | Solid fills — buttons, badges, overlays. Highest chroma in the scale. |
| 10 | Hover state for a step-9 fill |
| 11 | Low-contrast colored text on surfaces (outline buttons, links, labels) |
| 12 | High-contrast text |
Foreground text on step-9 fills: use white for most scales. Use a dark foreground (step 12 or --color-gray-12) only for the bright scales: Lime, Yellow, Amber, Sky, Mint — their step-9 is light enough that white text fails contrast.
Semantic tokens
What components actually consume. Re-skinning a project = reassigning these.
Every name reads color → property → intention → variant → state, and the property is always one of background, content or border. So the token you want is guessable: a danger border is --color-border-utility-danger, never --color-danger-border.
--color-background-*
Fills and surfaces — the colour behind things.
--color-content-*
Text, icons and SVG strokes — the colour of things sitting ON a background.
--color-content-on-*
Foreground ON the matching solid fill. One per intention — the bright ramps (lime, yellow) need dark text where the rest take light, and hardcoding #fff is how that gets missed.
--color-border-*
Strokes and dividers.
Theming
A spoke project overrides semantic roles under a scope; the brand changes everywhere at once, components untouched:
[data-theme="beacon"] {
--color-background-brand: #1f7a6d;
--color-background-brand-hover: #155a50;
--color-background-elevation-sunken: #f0f6f4;
}