Pick exactly one. Each direction file sets the same semantic roles (light + dark), so components never change when the direction does. Source of truth: packages/tokens/css/directions/ (template copy: src/styles/void/directions/). Read its header comment before building: it lists fonts, signature and don'ts.
Switching or installing a direction
/* src/app/globals.css — order matters */
@import "tailwindcss";
@import "../styles/void/base.css";
@import "../styles/void/directions/editorial.css"; /* exactly one */// src/app/layout.tsx — variable names MUST match the var() hooks in the direction file
import { Newsreader, IBM_Plex_Mono } from "next/font/google";
const serif = Newsreader({ subsets: ["latin"], variable: "--font-newsreader", display: "swap", style: ["normal", "italic"] });
const mono = IBM_Plex_Mono({ subsets: ["latin"], weight: ["400", "500"], variable: "--font-plex-mono", display: "swap" });
// <html className={`${serif.variable} ${mono.variable}`}>Then set direction: "editorial" in src/site.config.ts (theme-color, OG image and manifest read it) and theme.default to the direction's scheme.
Knobs each direction sets
| Knob | precision | editorial | swiss | warm | terminal | instrument |
|---|---|---|---|---|---|---|
--radius |
6px | 2px | 0 | 10px | 0 | 6px |
--weight-display / --weight-heading |
500 / 500 | 400 / 450 | 700 / 700 | 600 / 600 | 600 / 600 | 600 / 600 |
--tracking-display (× base tracking) |
1 | 0.35 | 0.9 | 0.6 | 1.1 | 0.5 |
--label-tracking |
0.08em | 0.1em | 0.06em | 0.06em | 0.12em | 0.06em |
| durations fast / base / slow / slower | 100/150/200/450 | 150/250/400/600 | 0/100/150/300 | 150/220/320/500 | 60/100/150/300 | 100/150/200/300 |
--grain-opacity light / dark |
0 / 0.035 | 0.045 / 0.06 | 0 / 0 | 0.03 / 0.05 | 0 / 0.04 | 0 / 0 |
| brand (light) | indigo oklch(54.5% .19 272) |
oxblood oklch(46% .15 22) |
signal red oklch(56.5% .22 29) |
clay oklch(55% .155 40) |
amber oklch(80% .165 72) (dark text on solids) |
blue oklch(53% .17 255) |
| default scheme | dark | light | light | light | dark | system |
| extra | — | — | --ease-out: linear |
— | — | --section-y 40→72px, --container-page 90rem |
Serif and geometric faces need less negative tracking, which is why editorial is 0.35× and terminal 1.1×.
precision — calm, exact, fast
- Use: dev tools, AI/infra products, pro SaaS, technical launch pages. Avoid: playful consumer, long-form reading.
- Measured references: Linear (Inter 510, −0.022em, #08090a, rgba(255,255,255,.08) hairlines, 8px radius), Vercel (Geist 400, −0.06em, ring-as-border, blue on 2 elements), Raycast.
- Fonts: Geist (display 500, UI 13px) + Geist Mono (labels, IDs, kbd). ~51 KB.
tsx import { Geist, Geist_Mono } from "next/font/google"; const sans = Geist({ subsets: ["latin"], variable: "--font-geist-sans", display: "swap" }); const mono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono", display: "swap" }); - Signature details:
label-monoas the UI voice; 1px white-alpha hairlines; depth from lighter surfaces and inset top highlight;Kbdchips; two-tone H2s; a real product UI (or honest DOM mock) as the hero visual; brand on ≤ 2% of pixels. - Don't: gradient buttons, pill buttons everywhere, serif, illustrations, glass cards, a second accent.
<section className="relative overflow-clip border-b border-line-subtle">
<div aria-hidden="true" className="pointer-events-none absolute inset-x-0 top-0 -z-10 h-144 bg-[radial-gradient(60%_70%_at_20%_0%,color-mix(in_oklch,var(--brand)_12%,transparent),transparent_70%)]" />
<Container className="pt-16 pb-20 lg:pt-32 lg:pb-24">
<Eyebrow>Changelog · v2.4 shipped</Eyebrow>
<h1 className="mt-6 max-w-[16ch] text-display-lg text-fg">Deploy previews for every branch in 14 seconds.</h1>
<p className="mt-6 max-w-[44ch] text-lg text-fg-muted">Push a branch, get a URL. Logs, env and rollbacks live in one place.</p>
<div className="mt-10 flex flex-wrap items-center gap-6">
<Button href="/signup" size="lg">Start deploying</Button>
<code className="font-mono text-sm text-fg-muted">$ npx acme deploy</code>
</div>
<div className="mt-16 overflow-clip rounded-xl border border-line bg-surface shadow-3 lg:mt-24">{/* real product UI */}</div>
</Container>
</section>editorial — literary, considered, slow
- Use: publications, essays, research, studios, premium brands, long-form docs. Avoid: dense data apps.
- Measured references: Anthropic (ivory #faf9f5, serif body 20px, clay accent on 1 element, split hero), Granola (serif display 86px at 0.93 leading, −0.02em, sections 112–160px), Paco (1.75 body leading, serif italic emphasis).
- Fonts: Newsreader (opsz axis: display and text from one file; italic for decks) + IBM Plex Mono (kickers, bylines). Snippet at top of this file.
- Signature details: body
text-lg(17–19px) at 1.6–1.65 inmax-w-prose; structure from rules (border-t border-line-strongon section heads) not boxes; mono uppercase kickers; always-underlined links (TextLink,decoration-brand-line underline-offset-4); italic deck under the H1; drop cap (first-letter:float-left first-letter:mr-2 first-letter:text-[3.6em] first-letter:leading-[0.8]); grain on paper. - Don't: cards, pills, icon grids, sans headlines, gradients, scaling or bouncy motion. Danger shares the red family with the brand: pair it with an icon and text.
<Container className="grid gap-10 border-b border-line-strong pt-20 pb-16 lg:grid-cols-12 lg:pt-32">
<div className="lg:col-span-7">
<p className="label-mono text-fg-muted">Essay · 14 min read</p>
<h1 className="mt-5 text-display-xl text-fg">The quiet economics of maintenance</h1>
</div>
<p className="text-xl italic text-fg-muted lg:col-span-5 lg:self-end">
Why the cheapest code is the code you already understand, and what it costs to forget that.
</p>
</Container>swiss — rational, confident, poster-like
- Use: agencies, architecture, type/design tools, events, portfolios, data journalism. Avoid: soft consumer brands.
- Measured references: basement.studio (one weight 600 at 0.90 leading, −0.04em, one #ff4d00 accent + selection, radius 0), hellohello (one weight, size-only hierarchy, numbered editorial lists with hairlines), PlanetScale (0 radius, logo table).
- Fonts: Schibsted Grotesk (700 display, 400 body) + JetBrains Mono (indices "01 —", captions).
tsx import { Schibsted_Grotesk, JetBrains_Mono } from "next/font/google"; const sans = Schibsted_Grotesk({ subsets: ["latin"], variable: "--font-schibsted", display: "swap" }); const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains-mono", display: "swap" }); - Signature details: 12-col grid with label col-span-3 / content col-span-9; flush left, ragged right;
border-lineis full-contrast ink here; numbered sections; huge size contrast (text-sm next to text-display-xl); hover = invert (hover:bg-fg hover:text-bg); radius 0; linear, near-instant motion. - Don't: shadows, any rounding, centered body, more than 2 weights, decorative icons.
<Container className="grid gap-y-8 border-t border-line pt-6 pb-24 lg:grid-cols-12">
<p className="font-mono text-sm text-fg-muted lg:col-span-3">01 — Studio</p>
<h1 className="text-display-xl text-fg lg:col-span-9">We design buildings that age in public.</h1>
<p className="max-w-[48ch] text-md text-fg-muted lg:col-span-5 lg:col-start-4">Forty-one projects across nine cities since 2011. Housing, libraries, one bridge.</p>
</Container>warm — approachable, tactile, human
- Use: consumer SaaS, productivity/notes, education, health, community. Avoid: security/infra, luxury, dense data.
- Measured references: Cursor (warm #f7f7f4, fg at 60/50/40%), Family (40–72px device radii, 1s out-expo reveals), Granola (olive accent, positive small-size tracking).
- Fonts: Bricolage Grotesque (display ≥ 32px only; never request extra axes: 40 → 128 KB) + Figtree (UI/body) + JetBrains Mono (
preload: false).tsx import { Bricolage_Grotesque, Figtree, JetBrains_Mono } from "next/font/google"; const display = Bricolage_Grotesque({ subsets: ["latin"], variable: "--font-bricolage", display: "swap" }); const sans = Figtree({ subsets: ["latin"], variable: "--font-figtree", display: "swap" }); const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-jetbrains-mono", display: "swap", preload: false }); - Signature details: cards
surface-cardonbg-bg-subtlewith 24px padding;bg-brand-subtle text-brand-textcallouts; sentence case;ease-springfor panels,ease-spring-bouncyonly for success moments; grain on hero; one illustration style (duotone brand + fg) if any. - Don't: neon, pure grays, sharp corners, 3D blobs, heavy shadows, Poppins/Nunito.
<Container className="grid items-center gap-12 pt-16 pb-20 lg:grid-cols-12 lg:pt-28">
<div className="lg:col-span-6">
<h1 className="max-w-[14ch] text-display-lg text-fg">Notes that write the follow-up for you.</h1>
<p className="mt-5 max-w-[42ch] text-lg text-fg-muted">Granular enough for a 1:1, calm enough for a Monday. Works offline.</p>
<Button href="/download" size="lg" className="mt-8">Download for Mac</Button>
</div>
<div className="surface-card p-6 lg:col-span-6">{/* real note UI */}</div>
</Container>terminal — raw, fast, honest
- Use: CLIs, dev infra, hacker tools, hackathons, developer launch pages. Avoid: mainstream audiences, long reading.
- Measured references: PlanetScale (system mono, 0 fonts, 1.7 MB page), the owner's DevEx landing (boot-log hero, amber at 1–2% coverage, tmux status line).
- Fonts: Geist Mono only (~23 KB). Alt: JetBrains Mono under the same variable name.
tsx import { Geist_Mono } from "next/font/google"; const mono = Geist_Mono({ subsets: ["latin"], variable: "--font-geist-mono", display: "swap" }); - Signature details:
$/>prompts intext-brand-text; bracketed actions[ deploy ]; hard offset shadow (shadow-3) on one focal element; solid 1px borders; amber solids take dark text (text-fg-on-brandhandles it); warning is yellower so "pending" never reads as the CTA; box-drawing dividers; tabular timestamps; a blinking block caret in CSSsteps(1). - Don't: rounded corners, soft shadows, gradients, serif, emoji, glass, typewriter headlines (type the log, not the H1).
<Container className="pt-16 pb-20 lg:pt-28">
<p className="label-mono text-fg-subtle">acme/cli · v3.2.0 · MIT</p>
<h1 className="mt-6 max-w-[18ch] text-display text-fg">Ship infra from a single YAML file.</h1>
<div className="mt-10 max-w-xl border border-line bg-surface p-4 font-mono text-sm shadow-3">
<p><span className="text-brand-text">$</span> curl -fsSL acme.sh | sh</p>
<p className="text-fg-muted">✓ installed acme 3.2.0 in 4.1s</p>
</div>
</Container>instrument — dense, data-first
- Use: analytics, admin, finance, ops consoles, internal tools, docs for data products. Avoid: marketing landing pages with big type.
- Measured references: Attio (14px w500 workhorse, 0.95 display leading, navy-tinted shadows, blue-tinted neutrals), Supabase (oklch tokens, 14px w450).
- Fonts: IBM Plex Sans + IBM Plex Mono (weights 400/500).
tsx import { IBM_Plex_Sans, IBM_Plex_Mono } from "next/font/google"; const sans = IBM_Plex_Sans({ subsets: ["latin"], variable: "--font-plex-sans", display: "swap" }); const mono = IBM_Plex_Mono({ subsets: ["latin"], weight: ["400", "500"], variable: "--font-plex-mono", display: "swap" }); - Signature details: 13–14px UI; 36px table rows; sticky table header on
bg-bg-subtle; right-alignedtabular-nums slashed-zero; delta chipsBadge tone="success"; panels split byborder-line-subtle; shadows only on floating layers; filters in the URL;section-yis compact (40→72px). - Don't: hero-size type, decorative gradients, scroll reveals, a second accent in chrome (charts use a separate palette).
<Container className="py-10">
<div className="flex items-end justify-between border-b border-line-subtle pb-4">
<h1 className="text-2xl text-fg">Revenue</h1>
<p className="font-mono text-xs text-fg-muted tabular-nums">Updated 09:41 UTC</p>
</div>
<dl className="grid grid-cols-2 gap-px bg-line-subtle lg:grid-cols-4">{/* KPI tiles: bg-bg p-4 */}</dl>
</Container>Blending rule
Never blend two directions. If the brief pulls two ways, pick the one matching the audience and borrow at most one detail from the other as a documented override in globals.css (for example editorial's italic deck inside precision). More than one override means you picked the wrong direction.