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 roles
What components actually consume. Re-skinning a project = reassigning these.
Theming
A spoke project overrides semantic roles under a scope; the brand changes everywhere at once, components untouched:
[data-theme="beacon"] {
--color-primary: #1f7a6d;
--color-primary-hover: #155a50;
--color-surface-sunken: #f0f6f4;
}