The system
This site, documented from its own tokens
Not a picture of the design system – the system itself. Every swatch and sample below renders from the same custom properties the rest of the site consumes, and the values are read live from the page. Change a token, and this documentation changes with the product, because it is the product. Toggle the theme (top right) and watch the values swap.
Three layers, one direction
Primitives (raw scales like --gray-400) feed semantic aliases (roles like --border-default), which feed components. Components only ever consume roles – that's what a semantic colour token buys: the whole site flips light to dark by swapping primitive values under stable role names, and red can never quietly become decoration.
Colour primitives – the number encodes the state
The scale convention is adopted from Vercel's Geist system (primary-sourced): the step number tells you which interface state it's for, so "how much darker is hover?" is never a judgement call again.
Gray – cool neutral, carries the whole interface
--gray-100default background--gray-200hover background--gray-300active background--gray-400default border--gray-500hover border--gray-600active border--gray-650input borders – a demand-grown step (≥3:1, SC 1.4.11)--gray-700high-contrast background--gray-800hover high-contrast background--gray-900secondary text & icons--gray-1000primary text & icons
Indigo – the one accent (Linear lineage, chosen at checkpoint 2)
--indigo-100default background--indigo-200hover background--indigo-300active background--indigo-400default border--indigo-500hover border--indigo-600active border--indigo-700high-contrast background--indigo-800hover high-contrast background--indigo-900secondary text & icons--indigo-1000primary text & icons
Status families – partial by design
Green, amber and red ship only the steps components actually use, and grow when one needs more. Coverage honesty is a design-system virtue: an unused token is a promise nobody is keeping.
Success
--green-100--green-400--green-700--green-900
Warning
--amber-100--amber-400--amber-700--amber-900
Danger
--red-100--red-400--red-700--red-900
Semantic aliases – what components actually touch
--bg-pageThe page itself--bg-raisedCards, panels, anything sitting on the page--bg-activePressed and selected surfaces--bg-inverseInverted surfaces (the skip-link pill)--border-defaultHairlines – the site separates with borders, not shadows--border-hoverBorders under the cursor--border-activeBorders of engaged or emphasised edges--border-inputText-input boundaries – the 3:1 non-text bar, honoured--text-primaryWords that are the point--text-secondaryWords that support--text-inverseText on inverted surfaces--accent-solidThe one accent: primary actions, the mark, the focus ring--accent-solid-hoverThe accent under the cursor--accent-bgAccent-tinted surfaces (callouts, featured cards)--accent-bg-hoverAccent-tinted surfaces under the cursor--accent-borderAccent-tinted borders (featured cards, the term underline)--accent-textAccent-coloured text and links--on-accentText sitting on the solid accent
Spacing – a 4px-base ladder
Nine rungs governing macro space – margins, gaps, panel padding: the distances that group things. Micro space (the optical padding inside a button, chip or badge) is tuned to the type it wraps instead, per the macro/micro distinction the grouping lesson teaches – a button's innards are a typography decision, not a layout one. The grouping lesson's page overlay lets you check the macro claim against this very site.
--space-1--space-2--space-3--space-4--space-5--space-6--space-7--space-8--space-9
Radius – Linear lineage
--radius-xs--radius-s--radius-m--radius-l--radius-fullType – two families, three weights, one scale
Satoshi (variable, self-hosted) for everything you read, Geist Mono for code. Weights stop at 600 – the restraint rule from the type lesson, held by convention rather than by a token (an honest design-system reality: not every rule is mechanically enforced, and knowing which ones aren't is part of reading a system). The scale is hand-tuned near a Major Third (modular scale) rather than generated, because optical sizes beat arithmetic at the extremes.
--text-xsDesign is decisions--text-smDesign is decisions--text-baseDesign is decisions--text-lgDesign is decisions--text-xlDesign is decisions--text-2xlDesign is decisions--text-3xlDesign is decisions--text-4xlDesign is decisions
Motion – feedback first, restraint always
Four durations, two curves – reveal exists only for narrative draws. Anything longer than 300ms had better be telling a story – and everything collapses to instant under prefers-reduced-motion, which is an accommodation, not a preference.
Elevation – three steps, hairlines still first
Borders group; shadows layer. Three steps: --shadow-1 for resting cards and controls, --shadow-2 for hover lift and emphasis,--shadow-3 for genuinely floating surfaces (the glossary popover, the release pills). Shadow values are literal rather than palette-derived on purpose: palette steps invert in dark mode, and an inverted shadow is a white glow. Floating surfaces add glass – translucency with backdrop blur – which is figure/ground doing work, and deliberately never sits under small text on busy backgrounds (Apple's 26.1/26.2 walk-backs are the cautionary tale, notes 09/13).
Component inventory – what's actually shipped
Live renders, not screenshots. The inventory grows as courses need components; nothing is documented before it exists.
Larger assemblies shipped so far: demo frame, quiz, lesson chrome, case embed, specimen stage – all visible in course 1 and the library, all built from the parts above.
The rules that keep it a system
- No raw colours, radii or durations in components. Anything with a hue, curve or clock is a token reference, and macro spacing rides the ladder. Two documented exemptions: micro padding inside controls (under Spacing), anddepictions – the Linear case vignette bakes its palette deliberately, for the same reason shadows stay literal: a picture of another product must not obey our theme. When a component needs something new, the token is added first – that's how the shadow, reveal-duration and xs-radius tokens arrived.
- Components consume roles, not primitives.
--gray-400belongs to the palette; components reach for--border-default. Roles are the API; primitives are the implementation. - One accent. Indigo does every "look here" job. Status colours mean status, never decoration – the colour lesson demonstrates exactly why.
- Coverage honesty. Scales ship the steps in use. Growth is demand-driven, documented by this page automatically –
--gray-650is the worked example: text inputs needed a boundary clearing the 3:1 non-text bar the colour lesson teaches, so the scale grew a step (closing the gap ADR #011 had put on the record). - Utilities read the tokens too. Tailwind's theme is bridged from these custom properties (
@theme inlinein global.css), so even utility classes can't smuggle in off-system values.