Foundations
Color
39 colour tokens in the base theme. Components only use these semantic roles, never the raw palettes underneath, which is what lets a brand or dark mode change every component at once. The swatches are painted from the live CSS variables, light on the left and dark on the right.
Tokens
Neutral scale
The gray ramp the other colour roles resolve to. It flips in dark mode, so 50 is always the page and 900 always the strongest contrast.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-neutral-50 | #FAFAFA | #171717 | — | |
--color-neutral-100 | #F5F5F5 | #262626 | — | |
--color-neutral-200 | #E5E5E5 | #404040 | — | |
--color-neutral-300 | #D4D4D4 | #525252 | — | |
--color-neutral-400 | #A3A3A3 | #737373 | — | |
--color-neutral-500 | #737373 | #A3A3A3 | — | |
--color-neutral-600 | #525252 | #D4D4D4 | — | |
--color-neutral-700 | #404040 | #E5E5E5 | — | |
--color-neutral-800 | #262626 | #F5F5F5 | — | |
--color-neutral-900 | #171717 | #FAFAFA | — |
Surface
Background layers, from the page itself to raised containers, plus an inverse surface for dark-on-light moments.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-surface-primary | #FAFAFA | #171717 | Card, Dialog, Drawer, Menu and 8 more | |
--color-surface-secondary | #F5F5F5 | #262626 | Card, Menu, NavigationMenu, Accordion and 13 more | |
--color-surface-tertiary | #E5E5E5 | #404040 | Dialog, Drawer, Table, Switch | |
--color-surface-inverse | #171717 | #FAFAFA | Tooltip |
Text
Primary for body copy and headings, secondary for supporting text, inverse for text on an inverse surface.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-text-primary | #0A0A0A | #FAFAFA | Dialog, Drawer, Menu, NavigationMenu and 17 more | |
--color-text-secondary | #525252 | #D4D4D4 | Card, Dialog, Drawer, Menu and 21 more | |
--color-text-inverse | #FAFAFA | #171717 | Tooltip |
Accent
Primary actions and selected states. In base it’s near-black; a brand changes these three to put its own colour on actions.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-accent-default | #262626 | #F5F5F5 | Card, Pagination, Table, Tabs and 7 more | |
--color-accent-hover | #171717 | #FAFAFA | Button, Link | |
--color-accent-foreground | #FFFFFF | #171717 | Pagination, Button, Checkbox, SkipLink and 2 more |
Border
Dividers and outlines, the focus ring, and the curtain colour used by route transitions.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-border-default | #E5E5E5 | #404040 | Card, Dialog, Drawer, Menu and 13 more | |
--color-border-strong | #A3A3A3 | #737373 | Card, Checkbox, Input, Radio and 3 more | |
--color-border-focus | #0A0A0A | #FAFAFA | Alert, Card, Dialog, Drawer and 19 more | |
--color-curtain | #D4D4D4 | #525252 | — |
Feedback
Error, success, warning and info. Each state has a background, a foreground and a border, used together by Alert, Toast and form errors.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-feedback-error | #c0392b | #f87171 | Menu, Input, Label, Textarea | |
--color-feedback-error-background | #FEF2F2 | #262626 | Alert, Menu, Badge | |
--color-feedback-error-foreground | #a93226 | #f87171 | Alert, Toast, Badge | |
--color-feedback-error-border | #c0392b | #f87171 | Alert, Toast, Badge | |
--color-feedback-success-background | #F0FDF4 | #262626 | Alert, Badge | |
--color-feedback-success-foreground | #15803D | #f0fdf4 | Alert, Toast, Badge | |
--color-feedback-success-border | #16A34A | #4ade80 | Alert, Toast, Badge | |
--color-feedback-warning-background | #FFFBEB | #262626 | Alert, Badge | |
--color-feedback-warning-foreground | #B45309 | #fffbeb | Alert, Toast, Badge | |
--color-feedback-warning-border | #D97706 | #fcd34d | Alert, Toast, Badge | |
--color-feedback-info-background | #EFF6FF | #262626 | Alert, Badge | |
--color-feedback-info-foreground | #1D4ED8 | #eff6ff | Alert, Toast, Badge | |
--color-feedback-info-border | #2563EB | #60a5fa | Alert, Toast, Badge |
Skeleton
The two colours a loading skeleton pulses between.
| Token | Preview | Light | Dark | Used by |
|---|---|---|---|---|
--color-skeleton-base | #E5E5E5 | #404040 | Skeleton | |
--color-skeleton-highlight | #F5F5F5 | #262626 | Skeleton |
Usage
Reach for the role, not the shade. A surface token on a background, a text token on text.
.notice {
background: var(--color-surface-secondary);
color: var(--color-text-primary);
border: var(--border-width-default) solid var(--color-border-default);
}
.notice__meta {
color: var(--color-text-secondary);
}
/* Dark mode needs no extra CSS: the same tokens
resolve to dark values under [data-mode="dark"]. */Do and don’t
Do
- Use
--color-text-secondaryfor supporting text, so it stays readable in both modes. - Pair each feedback background with its own foreground and border.
- Pair colour with text or an icon when it carries meaning, like an error.
Don’t
- Write a hex value. The linter rejects it in component CSS, and it won’t follow dark mode.
- Use a neutral step for a role that has its own token, like
--color-neutral-200for a border. - Use the accent for decoration. It marks the main action.