Colour, type, and the numbers underneath
Everything here is a CSS custom property on :root. The light theme redefines the same names under html[data-theme="light"] and changes nothing else — flip the toggle in the nav and watch the whole page follow.
Palette
Cool slate in the dark theme, warm stone in the light one. That is deliberate: inverting slate goes muddy, and stone gives the light theme its own character rather than making it read as the dark theme with the lights on. Click any swatch to copy its token.
Token reference
The complete set. Anything not on this list is a value someone hardcoded.
| Token | Dark | Light | Use |
|---|---|---|---|
| --bg | #020617 | #f5f5f4 | Page background |
| --panel | #0b1120 | #fafaf9 | Raised surface |
| --panel-2 | #0f172a | #e7e5e4 | Track and ground fills |
| --hairline | #1e293b | #bfbbb7 | Every border |
| --text | #f8fafc | #0c0a09 | Headings |
| --body | #cbd5e1 | #292524 | Interface copy |
| --ink / --ink-2 | #dde5ef / #9fb0c4 | #1c1917 / #44403c | Prose, secondary prose |
| --muted / --dim | #7c8ba1 / #64748b | #57534e / #78716c | Chrome only |
| --cyan | #67c7d4 | #0e7490 | The accent |
| --font-heading | Alliance No 1 | Headings, nav, UI labels | |
| --font-sans | Alliance No 2 | Body, small uppercase chrome | |
| --font-mono | Space Mono | Code, tokens, indices, figures | |
| --maxw | 1320px | Rail separation | |
| --gutter | clamp(1.25rem, 4vw, 2.8rem) | Padding inside the rails | |
| --band | 64px | Nav height, footer height | |
| --chrome | 130px | 64 + 1 + 1 + 64 — both hairlines | |
| --measure | 44rem | Reading column | |
| --radius-notch | 3px | The only radius in the system | |
| --t-fast / --t / --t-slow | 0.2s / 0.25s / 0.42s | Transitions | |
| --ease | cubic-bezier(.22,.6,.36,1) | The house curve | |
Contrast
On a near-black background these are easy to get wrong by eye — a grey that looks fine at 48px is unreadable at 14px. Compute the ratio. The two chrome greys are listed here precisely so nobody reaches for them by accident.
| Token | On --bg | Rating | May carry |
|---|---|---|---|
| --text | 19.4:1 | AAA | Anything |
| --body | 11.9:1 | AAA | Anything |
| --ink | 13.6:1 | AAA | Long-form prose |
| --ink-2 | 7.4:1 | AAA | Secondary prose |
| --muted | 5.8:1 | AA | Labels, chrome — not paragraphs |
| --dim | 4.2:1 | Fails AA | Decorative chrome only |
| --cyan | 9.7:1 | AAA | Links, active state |
--muted or brighter.Typography
Two faces and a mono. Alliance No 1 carries headings, nav and UI labels; Alliance No 2 carries body copy, and ships a single weight — emphasis switches face to No 1 rather than asking No 2 for a bold it does not have; Space Mono carries data only: code, token names, part numbers, indices and figures, never chrome labels. Both Alliance cuts are light-weight grotesques and read small for their nominal size — size up rather than down.
.prose, which steps the colour up to --ink and the leading to 1.8.Every number a reader compares down a column gets .num, which turns on tabular and lining figures (tnum, lnum, ss01). Tables do it automatically.
911,470
1,000,111
911,470
1,000,111
Space & geometry
There is no 4px/8px spacing scale to memorise. Layout space comes from three places: the --gutter inside the rails, the 96px grid module the background draws, and clamp() on anything that must survive from 320px to 2560px.
(100vw − 1320px) / 2 — and collapse to zero below 1320px. There is no fixed sidebar.clamp(1.25rem, 4vw, 2.8rem) — 20px on a phone, 44.8px on a desktop. Used for horizontal padding inside the rails and nothing else.clamp(1rem, 2vw, 1.8rem), which sits inside it comfortably at every width.calc(100vh − 130px). Count both hairlines — 129 overflows by exactly 1px and puts a scrollbar on a page that is supposed to be fixed.clamp(1.4rem, 3.5vh, 2.6rem); heading margins from the type's own leading. Nothing is set in fixed rem above 3rem.Motion
Short, eased, and always in service of a change of state. The house curve is cubic-bezier(0.22, 0.6, 0.36, 1) — quick to start, long to settle. Three durations cover everything.
| Token | Value | For |
|---|---|---|
| --t-fast | 0.2s | Colour and border changes on hover |
| --t | 0.25s | Popovers, tooltips, notch states |
| --t-slow | 0.42s | Panel slides, drawer track, bar growth |
prefers-reduced-motion: reduce collapses every animation and transition to 0.01ms and kills the headline sweep outright. You do not need to handle it per component.