Reference · tokens

Style guide

Every token on this site comes directly from tokens.css, so the page stays in sync with the system. There are two tiers: primitives hold the raw values, while semantics give components meaningful tokens to use. Everything is theme- and direction-aware. Press i and hover, or use the controls in the navigation, to see them resolve.

Primitives

The raw values: hex values, rem steps, font stacks, curve definitions. Nothing on the site references these directly except the semantic layer; if a component needs one, it asks through an alias.

Colour · Direction A (warm)

Both palettes ship. The Warm / Cool switch in the navigation flips the whole site between them, and every semantic token resolves against whichever set is live.

paper

  • --paper-50 #fcfaf5
  • --paper-100 #f7f4ed

cream

  • --cream-100 #f0ede5

ink

  • --ink-875 #21201a
  • --ink-900 #1c1b17
  • --ink-925 #1b1a16
  • --ink-950 #14130f

grey

  • --grey-300 #8c8a82
  • --grey-400 #ada89b
  • --grey-500 #6b675c
  • --grey-600 #5c5950
  • --grey-700 #4a4842

line

  • --line-100 #e7e2d6
  • --line-200 #d9d2c0
  • --line-700 #3e3b31
  • --line-800 #2b2922

rust

  • --rust-400 #c97a3f
  • --rust-600 #8c4d1f

Colour · Direction B (cool)

chalk

  • --chalk-50 #fafaf8
  • --chalk-100 #f4f4f2

frost

  • --frost-100 #ececec

carbon

  • --carbon-875 #1c1c1e
  • --carbon-900 #161616
  • --carbon-925 #161617
  • --carbon-950 #0f0f10

steel

  • --steel-300 #888888
  • --steel-400 #ababab
  • --steel-500 #676767
  • --steel-600 #5a5a5a
  • --steel-700 #484848

seam

  • --seam-100 #e4e4e2
  • --seam-200 #d2d2cf
  • --seam-700 #3c3c3f
  • --seam-800 #28282a

cobalt

  • --cobalt-400 #6b8cff
  • --cobalt-600 #2a4abd

Spacing scale

Nine steps, each drawn at true size. The semantic taxonomy assigns these to directions; the scale itself carries no meaning about where a gap goes.

  • --space-1 0.25rem · 4px
  • --space-2 0.5rem · 8px
  • --space-3 0.75rem · 12px
  • --space-4 1rem · 16px
  • --space-6 1.5rem · 24px
  • --space-8 2rem · 32px
  • --space-12 3rem · 48px
  • --space-16 4rem · 64px
  • --space-24 6rem · 96px

Type families

  • --font-sans Hanken Grotesk Variable stand-in for Söhne, the workhorse voice
  • --font-serif Season Serif Season Serif, the licensed display face, headlines
  • --font-mono-ui JetBrains Mono Variable stand-in for Söhne Mono: eyebrows, labels, table headers, key caps
  • --font-mono-data JetBrains Mono Variable JetBrains Mono: numerals, dates, token names, hex, code

Type sizes

  • --text-display clamp(2.25rem, 3.7vw, 4.25rem) · 36–68px
  • --text-heading clamp(1.875rem, 3vw, 3rem) · 30–48px
  • --text-body clamp(1.125rem, 0.75rem + 0.4167vw, 1.25rem) · 18–20px
  • --text-small clamp(0.9375rem, 0.75rem + 0.2083vw, 1rem) · 15–16px
  • --text-caption clamp(0.8125rem, 0.625rem + 0.2083vw, 0.875rem) · 13–14px

Weights

  • --weight-body 380 body, lead, small, meta
  • --weight-label 400 mono labels, captions, table headers
  • --weight-heading 400 serif headings (h1–h3)
  • --weight-action 450 buttons, segmented controls, metric numerals, list item names
  • --weight-strong 550 wordmark

Leading

  • --leading-tight 1.08 page headline (H1)
  • --leading-snug 1.12 headings (H2, H3), list item names
  • --leading-relaxed 1.6 body, lead, small

Tracking

  • --tracking-tight -0.01em wordmark
  • --tracking-heading 0.02em serif headings (h1–h3)
  • --tracking-ui 0.04em sentence-case mono in the controls
  • --tracking-wide 0.08em mono labels and table headers

Border widths

  • --border-width-sm 1px
  • --border-width-md 2px
  • --border-width-lg 3px

Radii

  • --radius-sm 2px
  • --radius-md 4px
  • --radius-pill 999px fully rounded ends: pills, scrollbar track and thumb

Motion

Hover or focus a row and the dot crosses its track at that token’s duration, on the standard curve. The Reduce motion switch (and the OS preference) stills them.

  • --motion-fast 150ms hovers, small state changes
  • --motion-standard 300ms panels, disclosure
  • --motion-slow 450ms large moves, page-level shifts
  • --ease-standard cubic-bezier(0.25, 0.1, 0.25, 1) the one easing curve, everywhere

Density

--density is a unitless multiplier: 1 by default and 0.7 when the Compact switch in the navigation is on. Every semantic spacing token runs through it, which is why the whole page tightens with one switch. Flip it and watch the spacing bars in the Semantics view resize.

Semantics

Semantic tokens give components names they can rely on, like text-strong, instead of raw values. Change the theme and the tokens change with it, without changing the components.

Colour

Swatches use the live custom properties, so they always reflect the active theme and palette. The token name updates with them. Use the controls in the navigation to switch modes and see the colours change.

  • --color-surface-page → --paper-100
  • --color-surface-raised → --paper-50
  • --color-surface-band → --cream-100
  • --color-text-strong → --ink-900
  • --color-text-default → --grey-700
  • --color-text-subtle → --grey-500
  • --color-link-underline → --grey-400
  • --color-border-strong → --grey-500
  • --color-border-default → --line-200
  • --color-border-subtle → --line-100
  • --color-accent-default → --rust-600
  • --color-focus-ring → --rust-600

Spacing

Six concepts replace a flat spacing scale, in two families. inset is space inside a box and compiles to padding; gap is space between boxes and compiles to gap. Each family has a both-axis token and two axis-specific ones. Every token uses the primitive scale and responds to the density setting, so the bars below show the actual size. Read more about the approach on spacing tokens.

inset internal, both axes. Card padding.

  • --spacing-inset-xs = --space-1 × density
  • --spacing-inset-sm = --space-2 × density
  • --spacing-inset-md = --space-4 × density
  • --spacing-inset-lg = --space-6 × density

inset-block internal, vertical. Content spacing inside a panel.

  • --spacing-inset-block-xs = --space-1 × density
  • --spacing-inset-block-sm = --space-2 × density
  • --spacing-inset-block-md = --space-3 × density
  • --spacing-inset-block-lg = --space-6 × density
  • --spacing-inset-block-xl = --space-12 × density

inset-inline internal, horizontal. Left and right padding in buttons.

  • --spacing-inset-inline-xs = --space-2 × density
  • --spacing-inset-inline-sm = --space-3 × density
  • --spacing-inset-inline-md = --space-4 × density
  • --spacing-inset-inline-lg = --space-8 × density

gap external, both axes. The grid and flex gap shorthand.

  • --spacing-gap-xs = --space-1 × density
  • --spacing-gap-sm = --space-2 × density
  • --spacing-gap-md = --space-4 × density
  • --spacing-gap-lg = --space-8 × density
  • --spacing-gap-xl = --space-16 × density
  • --spacing-gap-2xl = --space-24 × density

gap-block external, vertical. Rows, stacked sections, list items.

  • --spacing-gap-block-xs = --space-1 × density
  • --spacing-gap-block-sm = --space-2 × density
  • --spacing-gap-block-md = --space-4 × density
  • --spacing-gap-block-lg = --space-8 × density
  • --spacing-gap-block-xl = --space-16 × density
  • --spacing-gap-block-2xl = --space-24 × density

gap-inline external, horizontal. Gaps between adjacent controls.

  • --spacing-gap-inline-xs = --space-2 × density
  • --spacing-gap-inline-sm = --space-3 × density
  • --spacing-gap-inline-md = --space-6 × density
  • --spacing-gap-inline-lg = --space-12 × density

Text styles

Each type role in type.css combines the font family, size, weight, line height, letter spacing and colour into one class, much like a Figma text style. The examples use the actual type styles, so you can see exactly what each one looks like. Apply a .type-* class directly, or use one of the combined selectors.

Page headline

.type-display

Inner-page hero on Work, About, Contact, Thinking, and both homepage heroes (the full-bleed one caps the size against viewport height).

Font
var(--font-serif) · Season Serif
Size
var(--text-display) · clamp(2.25rem, 3.7vw, 4.25rem) · 36–68px
Weight
var(--weight-heading) · 400
Line height
var(--leading-tight) · 1.08
Tracking
var(--tracking-heading) · 0.02em
Ligatures
font-feature-settings: 'dlig' 1 · ff fi fl
Colour
var(--color-text-strong)
Applies to
.page-head h1, .hero h1, .hero-full h1

Section heading

.type-heading

Section headings in prose pages, and the article title on a homepage Thinking piece.

Font
var(--font-serif) · Season Serif
Size
var(--text-heading) · clamp(1.875rem, 3vw, 3rem) · 30–48px
Weight
var(--weight-heading) · 400
Line height
var(--leading-snug) · 1.12
Tracking
var(--tracking-heading) · 0.02em
Ligatures
font-feature-settings: 'dlig' 1 · ff fi fl
Colour
var(--color-text-strong)
Applies to
.prose h2, .idea h2

Subheading

.type-subheading

Sub-section headings within prose. Body size on purpose: the serif family and heading weight mark it, not a size step.

Font
var(--font-serif) · Season Serif
Size
var(--text-body) · fluid on large screens · 18–20px
Weight
var(--weight-heading) · 400
Line height
var(--leading-snug) · 1.12
Tracking
var(--tracking-heading) · 0.02em
Ligatures
font-feature-settings: 'dlig' 1 · ff fi fl
Colour
var(--color-text-strong)
Applies to
.prose h3

Name · list item title

.type-name

The name of one item in a set, sitting over its own description: client names in the contracts lists, the audit-framework phase titles, the token names in the spacing-tokens article. Sans is what separates it from Subheading. Both are body size, so family and weight are all either style has to work with.

Font
var(--font-sans) · Hanken Grotesk
Size
var(--text-body) · fluid on large screens · 18–20px
Weight
var(--weight-action) · 450
Line height
var(--leading-snug) · 1.12
Tracking
normal
Colour
var(--color-text-strong)
Applies to
.rows .client, .phases h3

Lead / intro

.type-lead

The standfirst paragraph under a page headline.

Font
var(--font-sans) · Hanken Grotesk
Size
var(--text-body) · fluid on large screens · 18–20px
Weight
var(--weight-body) · 380
Line height
var(--leading-relaxed) · 1.6
Colour
var(--color-text-default)
Applies to
.page-head .intro

Body

.type-body

Default running text. Reading measure caps at 38em.

Font
var(--font-sans) · Hanken Grotesk
Size
var(--text-body) · fluid on large screens · 18–20px
Weight
var(--weight-body) · 380
Line height
var(--leading-relaxed) · 1.6
Colour
var(--color-text-default)
Applies to
body, p

Detail / UI text

.type-detail

Nav links, list-row descriptions, secondary controls.

Font
var(--font-sans) · Hanken Grotesk
Size
var(--text-small) · fluid on large screens · 15–16px
Weight
var(--weight-body) · 380
Line height
var(--leading-relaxed) · 1.6
Colour
var(--color-text-default)
Applies to
nav a, .rows p (buttons use var(--weight-action))

Eyebrow / label

.type-label

Mono caption above headlines and table headers. The editorial caption voice.

Font
var(--font-mono-ui) · JetBrains Mono
Size
var(--text-caption) · fluid on large screens · 13–14px
Weight
var(--weight-label) · 400
Tracking
var(--tracking-wide) · 0.08em
Case
uppercase
Colour
var(--color-text-default)
Applies to
.label, .token-table th

Meta / mono data

.type-meta

Dates and tabular values in list rows. Tabular figures keep columns aligned.

Font
var(--font-mono-data) · JetBrains Mono
Size
var(--text-caption) · fluid on large screens · 13–14px
Weight
var(--weight-body) · 380
Numerics
tabular-nums
Colour
var(--color-text-default)
Applies to
.rows .meta

Metric

.type-metric

The big accent numeral in a stats row: case-study outcomes, the About page facts.

Font
var(--font-mono-data) · JetBrains Mono
Size
var(--text-heading) · clamp(1.875rem, 3vw, 3rem) · 30–48px
Weight
var(--weight-action) · 450
Numerics
tabular-nums
Colour
var(--color-accent-default)
Applies to
.metrics .num, .facts .num

Wordmark

.type-wordmark

The studio name in the header.

Font
var(--font-sans) · Hanken Grotesk
Size
var(--text-small) · fluid on large screens · 15–16px
Weight
var(--weight-strong) · 550
Tracking
var(--tracking-tight) · -0.01em
Colour
var(--color-text-strong)
Applies to
.wordmark (header layers its own primitive/semantic colour)