Tokens
How color, light, radius and motion are organised.
Tokens come in two layers. The palette holds raw values and nothing else; the
semantic layer names roles and points them at palette steps. A component only
ever references a role, so retheming means editing the semantic layer and
nothing under components/.
/* layer 1: the only place a hex lives */
--neutral-900: #171717;
/* layer 2: roles */
--brand: var(--neutral-900);
--ring: color-mix(in oklab, var(--brand) 45%, var(--background));Palette
One achromatic ramp, --neutral-50 to --neutral-950, plus four status
ramps: green, amber, blue and red. There is no decorative hue. Color in Blank
means a status, a data delta or your brand, nothing else.
Two accents
primary is the action color: the filled button. brand is the identity
color: focus rings, selection, checked marks, active indicators, progress.
Both start as the same ink, so the system reads black and white until you
turn brand. Theming shows how.
Colors
Surfaces
--background--card--popover--muted--accent--sidebar--invertedAction
--primary--secondary--brand--brand-subtle--destructiveLines & fields
--border--input--input-field--input-hover--input-disabled--ringInteractive items
--item-outline--item-outline-hover--item-secondary--item-secondary-hover--item-selected--item-destructive-subtleStatus
--success--success-primary--warning--warning-primary--info--info-primary--danger--danger-primaryCharts use --chart-1 to --chart-5, steps of the ink from strongest to
faintest. A series that means up, down or failed uses the success and
danger roles instead.
Light
Raised surfaces are lit, not outlined. Two tokens carry the light:
| Token | Light | Dark | Used by |
|---|---|---|---|
--highlight | white | white at 7% | the top edge of a card: shadow-[inset_0_1px_0_var(--highlight)] |
--glow | ink at 6% | white at 14% | the soft light that falls into a bento card |
Radius
Everything derives from --radius (10px by default), so the system rounds
together. Controls are pills (rounded-full) at any radius.
| Token | Value | Used by |
|---|---|---|
--radius-lg | var(--radius) | small surfaces |
--radius-xl | calc(var(--radius) + 2px) | textarea, menu and select items |
--radius-2xl | calc(var(--radius) + 6px) | card, popover, menu, toast, alert |
--radius-3xl | calc(var(--radius) + 10px) | bento card |
--radius-4xl | calc(var(--radius) + 14px) | dialog, drawer, sheet |
--radius-full | 9999px | buttons, inputs, tabs, badges |
Motion
Three easings live in the theme, so they are Tailwind utilities
(ease-out, ease-in-out, ease-spring). Every transition-* utility
uses ease-out unless told otherwise.
| Token | Curve | Used for |
|---|---|---|
--ease-out | cubic-bezier(0.23, 1, 0.32, 1) | anything that enters or reacts |
--ease-in-out | cubic-bezier(0.77, 0, 0.175, 1) | anything that moves on screen |
--ease-spring | cubic-bezier(0.34, 1.56, 0.64, 1) | a press, a pop, the switch thumb |
Feedback takes 100–150ms, menus open in 150ms and close in 100ms, panels slide in 250–300ms.
Inverted surfaces
A dark menu on a light page is a surface, not a color family. Set
data-inverted on a popover-like element and it re-points --popover,
--muted-foreground, --border and the item states at their inverted
counterparts — every part inside keeps working unchanged.
<SelectContent inverted>…</SelectContent>