Theming
Two token layers: neutral surfaces that build a depth ladder, and eight colour sets any component can be pointed at with one prop.
Colour sets
A colour set assigns six --ui-* variables. Every colourable variant reads them, so one prop restyles solid, secondary, outline and ghost together.
One prop, four variants
tsx
<Button color="blue">Solid</Button> <Button color="blue" variant="secondary">Secondary</Button> <Button color="blue" variant="outline">Outline</Button> <Button color="blue" variant="ghost">Ghost</Button> // Or any CSS colour at all — fill, text, hover and border are derived: <Button tint="#7c3aed">Custom</Button>
neutralbluevioletcyangreenamberrosedestructive
Hover is a token, not a calculation
Hover values are declared per theme rather than derived by mixing. A mix has to pick a direction, and the right direction flips: a light fill darkens, a dark one lightens. One formula is wrong in one theme.
The surface ladder
With no shadows, depth is carried by lightness alone. Each step sits above the last:
| Surface | Dark | Used by |
|---|---|---|
| page | 0.08 | --background |
| card body | 0.13 | --card, --popover |
| card header | 0.15 | muted at 40% over the card |
| group | 0.18 | --secondary, the tray behind cards |
| hover | 0.20 | --accent |
Lowering opacity does not lighten
On a dark theme an opacity modifier blends toward the page, which pulls lightness down. Reaching for
/50 to lighten a surface is how the group once ended up darker than the card header it sits above.