Tokens
tier 2 — classesEvery colour, size, radius and duration in the stylesheet resolves through one of these, and all of them are below. The values are read from the stylesheet the browser actually loaded, so they follow the theme, the colour-blind palette and any override an app has applied.
brand.css
Load it after the library sheet.
Never declare a -- name the library does not already define: a future version may
introduce that name with a different meaning, and your app breaks on upgrade.
Getting started has the block to copy.
One component, not the whole theme
A shared role moves everything that paints with it: --border is every divider as
well as every field. Where an app needs one part to differ, it has a role of its own that
points at the shared one until redefined — --field-border and
--field-border-focus for the boxes a reader types into, --selected
for the mark on the one chosen, --tab-indicator for the line under the current
tab, --topbar-bg, --topbar-fg, --topbar-muted and
--topbar-hover for the header, --sidebar-muted beside
--sidebar-bg and --sidebar-fg for the sidebar,
--field-border-danger and --field-border-warn for a rejected and an
unusual field, --placeholder for the words in an
empty field, --brand-edge for a filled primary button's outline and
--focus-ring for the ring a focused control draws, --control-border
for the edge of a checkbox, radio and switch, --disabled-fg,
--disabled-bg and --disabled-border for a disabled control, and
--disabled-opacity for how far a disabled control's graphic fades. A text role still has to
clear 4.5:1 on --bg and --card-bg after you move it, and an edge
3:1.
/* brand.css, after the library sheet: a red brand on a wall display, where a red
ring round the record on show would read as a failure. */
:root {
/* Calm lines between cards, firm edges on the fields. */
--field-border: var(--slate-500);
--field-border-focus: var(--fg-soft);
/* The one on show in the page's own ink, the current tab still in the brand. */
--selected: var(--fg);
--tab-indicator: var(--brand-soft);
/* A disabled "Delete (0)" that can still be read. */
--disabled-opacity: 0.6;
}
/* A brand-coloured header in the light variant. */
:root[data-variant="light"] {
--topbar-bg: var(--brand);
--topbar-fg: var(--on-solid);
--topbar-muted: var(--on-solid);
--topbar-hover: var(--brand-hover);
}
Typography
Surfaces, text, lines
Named surfaces and elevation
Brand — the app override point
Selection
Fields
Disabled
Disabled graphics
Sidebar
Topbar
Sizes
Panel bars
Status — go (sends outward)
Status — warn (control changes)
Status — danger
Status — info & secret
Severity — an ordered five-step ramp
Categorical badge hues
Component surfaces
Shadows
Reconnect banner
Spacing scale
Type scale
Control height
Choice inset
Corner rounding
Motion
Density and metrics
Safe area
What this device has taken from each edge: 0px on a desktop and wherever nothing is in the way, the home indicator, notch or rounded corner on a phone or a tablet. A rule that adds the token is right on both without asking what it is running on; the host page needs viewport-fit=cover for the values to arrive at all.
Data visualisation
Data in motion
Agents
Agent avatar tones
Voice level
Unset until the app writes one, from 0 to 1, on any element above the parts that follow it.