Typography
One family, Geist, in sans and mono. Each text-* size carries its own line-height and tracking, and the heading sizes carry the heading weight, so one class sets a whole role.
Usage
Pair font-heading with a size from text-xl up for headings. Copy takes a size and nothing else; labels add a weight.
// Headings: font-heading marks the role; text-<size> carries size,// line-height, tracking and the heading weight together.<h1 className="font-heading text-3xl md:text-4xl">Agents</h1><h2 className="font-heading text-2xl">Recent runs</h2> // Copy: size only. Weight stays at 400 unless the text names a control.<p className="text-base text-muted-foreground">Ask about a run or a file.</p><p className="text-md text-foreground-low">Updated 2 hours ago</p> // Labels and data<span className="text-sm font-medium">Share profile</span><span className="text-label-12-caps text-foreground-low">Suggested</span><code className="text-label-14-mono">npm run dev</code>Families
Loaded once by next/font on <html>; the stacks fall back to the system UI face.
| Example | Token | Usage |
|---|---|---|
| Geist Sans | Prose, headings, labels, controls, tables and figures. The default on <html>.Geist · next/font | |
| Geist Heading | Marks a heading in markup. An alias of the sans today: the one line to point at a display face.var(--font-sans) | |
| Geist Mono | Code, commands, paths, raw tokens, timestamps and short identifiers. Never as a technical costume.Geist Mono · next/font |
Weights
Four weights and nothing else. Hover and selected states never change weight.
| Example | Class name | Usage |
|---|---|---|
| Regular 400 | Copy, labels that describe, table cells.--font-weight-normal · 400 | |
| Medium 500 | Buttons, chips, tabs, labels that name a thing, strong inside a label.--font-weight-medium · 500 | |
| Strong 550 | Strong inside running copy, a half step above the text around it.--font-weight-strong · 550 | |
| Semibold 600 | Every heading role. Small headings at body size set it by hand.--font-weight-semibold · 600 | |
| Heading weight | The knob the heading sizes read (text-xl and up, text-display, heading classes, prose headings).var(--font-weight-semibold) |
Headings
Semibold, tightening as they grow: −0.02em at 20px, −0.06em from 40px.
| Example | Class name | Usage |
|---|---|---|
| Heading 48 | The one page-defining statement. Fluid from 40 to 48px with the viewport.40→48 / 1.15 · −0.06em · 600 | |
| Heading 48 | Display statement at a fixed size: marketing heroes, empty-state headlines.48/56 · −0.06em · 600 | |
| Heading 40 | The page title from md up.40/48 · −0.06em · 600 | |
| Heading 32 | Page title on narrow screens, tile names.32/40 · −0.04em · 600 | |
| Heading 24 | Major section turns, dialog titles. The h2 on these docs pages.24/32 · −0.04em · 600 | |
| Heading 20 | Nested structure, subsection headings, answer h2.20/26 · −0.02em · 600 | |
| Heading 18 | Row and card titles.18/28 · −0.02em · 600 | |
| Heading 16 | A heading that sits at body size. No token of its own; tracking stays at 0.16/24 · 0 · 600 | |
| Heading 14 | Group headings in dense UI: settings sections, panel titles.14/20 · 0 · 600 |
Copy
Regular weight, zero tracking. Hierarchy in copy comes from size and the text tier, not weight.
| Example | Class name | Usage |
|---|---|---|
| Copy 18, a short orientation passage. | Lede: one passage under a page title. The lead on these docs pages from md.18/28 · 0 · 400 | |
| Copy 16, the body of an answer. | Body, the composer, suggested questions, chat messages.16/24 · 0 · 400 | |
| Copy 14, dense UI and table cells. | Secondary copy in panels, table cells, settings descriptions.14/20 · 0 · 400 | |
| Updated 2 hours ago · 3 collaborators | Metadata and captions, usually on the third text tier.13/18 · 0 · 400 | |
| By continuing you agree to the Terms. | Footers, disclaimers, keycaps. The smallest size the app sets.12/16 · 0 · 400 | |
| Copy with strong emphasis. | Strong inside copy. 550, not 600, so emphasis never reads as a heading.inherits size · 550 |
Labels
Short text that names or measures something: controls, group labels, identifiers, figures.
| Example | Class name | Usage |
|---|---|---|
| Share profile | Button, chip and tab labels; a label that names a thing.14/20 · 0 · 500 | |
| Behavior design | Small control labels and a card's sentence-case label.12/16 · 0 · 500 | |
| Suggested questions | Uppercase label over a group of cards, table headers. Tracking travels with the caps.12/16 · +0.06em · 500 | |
| npm run brand:check-contrast | An identifier, command or token set in mono. Tabular figures, slashed zero.14/20 · mono · 400 | |
| v2.4.0 · 1,234.56 ms | Figures and provenance only: a measured number, a version, a citation.12/16 · mono · 400 |
Best practices
Hierarchy
- One text-heading-display or text-4xl per page; step down one size per level.
- Separate levels with size and the text tier (foreground, muted-foreground, foreground-low) before weight.
- Hold copy to a 65ch measure; balance headings with text-balance and wrap copy with text-pretty.
Mono and figures
- Mono is for code, commands, tokens and measured figures. A label that names a thing stays in the sans.
- Numbers that change in place (counters, timers, prices) use a mono label class or tabular-nums so nothing shifts.
- Never invent a weight: anything outside the four above fails npm run brand:lint-tokens.