What changed
v2 had the right devices and no budget
The previous system shipped gradient borders and coloured glows and ambient light and two competing accents and per-module feature hues, all at once. Every device was defensible on its own. Together they read as noise.
The structural fix
Colour became measurable
v2 stored colour in HSL, where lightness is not perceptual. A warm accent and a cool accent at the same L% land at visibly different weights, so the two could never be balanced by hand. v3 stores everything in OKLCH, where equal numbers look equal.
The behavioural fix
Every device got a ration
One gradient per viewport, and it has to read as light. Accent on at most 5% of the pixels. Two live hues, maximum. One radius scale. One focus treatment. The rules are countable, which means /design-review can enforce them.
| Token | Value | Use |
|---|---|---|
| Gradient borders | retired | The single strongest "vibe-coded" tell. Replaced by a bevel. |
| Pill buttons | retired | Pills are for badges and chips only. Buttons use --r-2. |
| Warm + cool pair | retired | One accent. A second hue is a feature mark, never a co-star. |
| Hue-tinted surfaces | retired | Feature hues mark identity — a rule, a dot, a tick. Never a wash. |
| Breakpoint sizing | replaced | Fluid clamp() scales for type and space. Nothing snaps. |
| Ad-hoc durations | replaced | Five duration tokens and one house curve, shared by CSS and JS. |
Foundations
Colour
A near-monochrome canvas where colour is an event. Every neutral carries a whisper of the project hue — chroma capped at 0.014, which is the number that separates tinted from coloured.
0
1
2
3
4
5
6
7
8
9
10
11
12
Do
Spend the accent on one primary action, the active nav state, links, the focus ring, and one ambient light source. That is the whole permitted list.
Don't
Tint card backgrounds, borders of inactive elements, headings, or icons. If the element isn't the current subject, it is neutral.
The dial
Three numbers retint every token on this page.
--accent: 0.780 0.135 78
every other accent token derives from that line.
| Token | Value | Use |
|---|---|---|
| --bg | --n-0 | The canvas. Never #000 or #FFF. |
| --surface | --n-1 | Cards and panels — one ramp step up. |
| --surface-raise | --n-2 | Overlays, hover surfaces, secondary buttons. |
| --surface-sink | below --n-0 | Code wells — the only surface below the canvas. |
| --text / --text-muted / --text-faint | --n-11 / --n-8 / --n-7 | Three text weights. There is no fourth. |
| --line-subtle / --line / --line-strong | --n-3 / --n-4 / --n-5 | Hairlines. Depth comes from these, not shadow. |
Foundations
Type
Three voices, and hierarchy comes from voice and scale rather than weight. Nothing in the system is bold.
display · Inter opsz 32, weight 500
Hierarchy from scale
body · Inter, cv11 + ss01
Body copy sits at --step-0 with a 1.65 line-height and a 70ch measure. Long-form prose loosens to 1.72.
micro · JetBrains Mono, +0.14em
Section labels · eyebrows · counters · table headers
Foundations
Space & radius
Two viewport poles — 390px and 1440px — and everything between interpolates. Tailwind's own --spacing base unit is fluid, so every p-4 and gap-6 in the codebase breathes without a single edit.
Foundations
Depth
Depth is light physics: a bevel highlight where light would land, a hairline ring, and a two-step shadow ramp. Never a decorative gradient, never a soft grey blur.
Components
Controls
Every variant shares one anatomy, so they read as siblings rather than strangers. And every interactive element must express all five states from the same tokens — that contract is ship-blocking.
Do
Use exactly one primary per view. Two primaries means neither is — demote the second to secondary or outline.
Don't
Give a control a bespoke hover, radius, or focus ring. If a component needs to look different, it needs a token, not an exception.
Foundations
Motion
The part of v2 that was already right — kept, and made repeatable. Five durations, one house curve, and choreography rules that state why the old sites felt good so the next one does too.
Duration tokens
Distance scales with duration — that pairing is the rule.
- --dur-1120ms
- --dur-2200ms
- --dur-3320ms
- --dur-4520ms
- --dur-5900ms
Every row uses --ease-out, the house curve. Under prefers-reduced-motion all five snap to their end state.
Choreography
- Distance scales with duration. An 8px move gets
--dur-2; a 24px move gets--dur-4. - Two properties maximum per element — opacity plus one transform.
- Stagger units are 40ms inside a list, 70ms between sections, with the total capped at 400ms so long lists let their tail arrive together.
- One scene per page, not per section.
- Hover changes light, not layout. Glow, opacity, bevel — never a transform that reflows.
The signature startup
Every harness site opens with the same orchestrated anime.js timeline — the one that just ran above. The eyebrow settles, the display headline cascades character by character out of per-word clip masks, then sections stagger in as a wave. Under 1.2s.
// mark the hero, then call once on mount <p data-intro-eyebrow>…</p> <h1 data-intro-headline>…</h1> <section className="reveal">…</section> introTimeline(pageRef.current)
The page load belongs to anime.js. Motion keeps in-page presence and layout. Reduced motion snaps everything visible.
Inventory
Components
Built only from semantic tokens. A component that needs a raw value is a component that will drift.
Interactive
Hover me
Border, surface and lift shift together at --dur-2.
Static
Hairline + one step
No gradient wash. No accent border unless it is the active item.
Accent budget
5
% of viewport
Live hues
2
maximum
Gradients
1
per viewport
Nothing here yet
A designed zero-state is ship-blocking. An empty rectangle is a craft-floor failure.
Enforcement
The countable rules
A rule that can't be counted can't be enforced, and a design system nobody enforces is a mood board. These are what /design-review checks before /ship lets anything go live.
| Token | Value | Use |
|---|---|---|
| Gradient budget | ≤ 1 / viewport | And it must read as light — ambient field or mask fade. Zero gradient borders. |
| Colour budget | ≤ 5% accent | Accent pixels as a share of any viewport. Halved from v2's 10%. |
| Live hues | ≤ 2 | The accent plus at most one feature mark. Status colours are exempt. |
| Neutral chroma | ≤ 0.014 | Above this the canvas reads coloured rather than tinted. |
| Radius scale | 1 per view | Every button, input and menu item shares a radius. No stray pills. |
| Control states | 5 of 5 | rest · hover · press · focus-visible · disabled, from shared tokens. |
| Text weights | 3 | --text, --text-muted, --text-faint. There is no fourth. |
| Hard-coded px | 0 | Type and spacing come from steps. Fixed px only for control heights. |
Where this lives
DESIGN.md— the spec. Single source of truth; this site is its showcase.templates/nextjs-app·templates/vite-app— the executable copies. New projects start here.TOOLKIT.md— the library shortlist, and which registries are safe to pull from.