Skip to content

Foundations

Typography

Two families and nine steps. Each step binds size, line-height and tracking together, so the only decision left is which step — and the pairings below are the answers the system has already made, so that decision rarely has to be made twice.

The ramp

The quick brown fox

display 44px · One per page, marketing only

The quick brown fox

title1 31px · Page title

The quick brown fox

title2 24px · Card and section titles, big numbers

The quick brown fox

title3 20px · Sub-sections, dialog titles

The quick brown fox

headline 17px · Card headers, emphasised rows

The quick brown fox

callout 16px · Lead paragraphs

The quick brown fox

body 15px · Everything read as language

The quick brown fox

footnote 13px · Secondary text, hints, metadata
The quick brown fox caption 11px · Eyebrows and labels — uppercase, tracked

Headings tighten as they grow; labels go the other way — small, uppercase, widely tracked. Both moves are inside the variant, so a heading cannot accidentally be set with body tracking.

Sans or mono

One question decides it: who wrote this? If a person did, it is Inter. If a machine emitted it, it is mono. If a person will compare it down a column, it is Inter with tabular figures — which aligns just as well as mono and does not turn a metrics panel into a terminal.

Inter

default
  • Prose, descriptions, error messages — anything read as language
  • Titles, labels, button text, navigation
  • Names of people, brands, channels, files as a user calls them
  • Numbers a human compares column-wise — with `numeric`, not with mono

Geist Mono

machine
  • Identifiers and hashes: run_8f2c41d, ord_19f4
  • Paths, filenames as the system stores them, env keys
  • Machine timestamps and durations: 09:14:22Z, 38.1s
  • Keyboard shortcuts, via Kbd
  • Log lines, payloads, code
  • Spec values in a technical strip: “Plan in < 90s”

Never mono

stop
  • Currency and KPI values — €49, 184.2k. They are read, not parsed
  • Body copy or headings, however technical the product is
  • Button labels and navigation
  • A whole table because one of its columns is machine output

Right

Revenue

€184,200

Run

run_8f2c41d

Money in Inter with tabular figures; the id in mono. Two voices, each doing its job.

Wrong

Revenue

€184,200

Run

run 8f2c41d

Money in mono reads as a log line; the id in sans invites you to read it as a word. Both are legible and both are wrong.

Pairings

Ten combinations that cover almost every screen. Each prints its own recipe: the variants, in order, as you would write them.

Showcase

Editorial plan

Twelve posts for week 34, grounded in the brief you approved on Monday.

Page header

caption → title1 → callout muted

The eyebrow classifies, the title names, the lead sells. Skipping the eyebrow is fine; skipping the lead leaves the title floating.

Queue

Four posts waiting to go out this week.

Section header

title3 → footnote muted

Inside a page. Never use title1 twice on one screen — a second one reads as a second page.

Autopilot

Produce, schedule and publish without waiting for approval.

Card header

headline → footnote muted

Cards sit in grids, so their titles stay one step below the section that contains them.

Impressions

184.2k

+12.4%

Stat tile

caption → title2 numeric → badge

Label first because the number is meaningless without it. Tabular figures so a row of tiles aligns.

Carousel · behind the collection Instagram · today 18:00

List row

body → footnote muted → footnote value

Title, context, trailing value. The value is right-aligned so the eye can scan a column of them.

media.render

reel/studio-tour · 38.1s · exit 0

Technical row

body → mono footnote

Same shape as a list row, but the second line is machine output, so it changes voice rather than just colour.

Shown to your clients

Form field

footnote medium → body input → footnote muted

Label above, hint below, both smaller than the value being typed. The value is the thing, so it is the largest.

No posts yet

Connect a channel and Anomalia writes the first plan.

Empty state

headline → footnote muted → button

Short. An empty state that explains itself in three lines is an empty state nobody reads.

Social media autopilot

Ship a month
in an afternoon.

Marketing hero

caption → display → callout muted

The only place display appears. Tracking is already tight at 44px, so it needs no manual adjustment.

92%

Approved unchanged

6h

Saved per week

Metric strip

title1 numeric → caption

Inverted order: on a marketing page the number is the headline and the label is the footnote.

Rules that keep it from unravelling

  • Two steps apart, at most. A title next to its supporting line should be one or two steps up — title3 over footnote. Four steps apart reads as two unrelated blocks.
  • One display per page, one title1 per screen. A second one is not emphasis, it is a second page.
  • Colour is the third axis, after size and weight. Reach for tone="muted" before reaching for a smaller step: shrinking secondary text twice is how a screen ends up with 11px paragraphs.
  • Caption is a label, not small body. It is uppercase and tracked, so it works for one to three words and fails for a sentence.
  • Mono never sets a heading. If a heading is an identifier — a run id, a file name — set it in sans and put the mono form underneath.
  • Shortcuts always go through Kbd, so renders as caps rather than as literal text with a plus in it.

Providing the fonts

The package names both families and ships neither: bundling a font fights whatever the host app already loads. Self-host Inter and Geist Mono in your app, or point --an-font-sans and --an-font-mono at what you already have — the x-height normalisation follows whatever resolves.

The system stack behind each name already works