---
title: "The six directions (craft)"
description: "Pick exactly one. Each direction file sets the same semantic roles (light + dark), so components never change when the direction does."
canonical: https://void-design.vercel.app/docs/craft/directions
lastModified: 2026-09-16
---

# The six directions

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/<name>.css` (template copy: `src/styles/void/directions/<name>.css`). Read its header comment before building: it lists fonts, signature and don'ts.

## Switching or installing a direction

```css
/* src/app/globals.css — order matters */
@import "tailwindcss";
@import "../styles/void/base.css";
@import "../styles/void/directions/editorial.css";   /* exactly one */
```

```tsx
// 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-mono` as the UI voice; 1px white-alpha hairlines; depth from lighter surfaces and inset top highlight; `Kbd` chips; 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.

```tsx
<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 in `max-w-prose`; structure from rules (`border-t border-line-strong` on 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.

```tsx
<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-line` is 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.

```tsx
<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-card` on `bg-bg-subtle` with 24px padding; `bg-brand-subtle text-brand-text` callouts; sentence case; `ease-spring` for panels, `ease-spring-bouncy` only 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.

```tsx
<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 in `text-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-brand` handles it); warning is yellower so "pending" never reads as the CTA; box-drawing dividers; tabular timestamps; a blinking block caret in CSS `steps(1)`.
- **Don't:** rounded corners, soft shadows, gradients, serif, emoji, glass, typewriter headlines (type the log, not the H1).

```tsx
<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-aligned `tabular-nums slashed-zero`; delta chips `Badge tone="success"`; panels split by `border-line-subtle`; shadows only on floating layers; filters in the URL; `section-y` is compact (40→72px).
- **Don't:** hero-size type, decorative gradients, scroll reveals, a second accent in chrome (charts use a separate palette).

```tsx
<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.
