FoundationsUpdated Sep 22, 2026
Typography
Eight sizes in two bands. Text sits at 18 and below, headings at 20 and above, so a heading never has to be told apart from a paragraph set in semibold. Every step carries its own leading and tracking, so one class is one decision.
Pick a role, then a weight. Never pick a leading or tracking by hand.
- A sentence is prose, anything else is UI. At a shared step they differ by figures and wrapping, not by size.
- Weight is separate. Roles never set it; add
font-mediumorfont-semibold. - There is no 14, 16 or 20. 14 is
sm, 16 ismd, 20 islg. An arbitrarytext-[…px]is a review flag. text-xs,text-sm,text-mdandtext-lgare the UI steps. They are the onlytext-*sizes:smis the 13px default, and there is notext-baseortext-xl. Each carries the UI role's leading and tracking, and as utilities they override a primitive's own size. Reach fortype-*when the role matters: prose, code, or a heading (type-h3totype-display).
// A role sets size, leading, tracking and figures together<span className="type-ui-sm text-foreground-secondary">Updated 2 min ago</span><p className="type-prose-md">Meeting notes stay in sync across devices.</p><h2 className="type-h2">Tuesday standup</h2> // Weight composes on top; the role never sets it<button className="type-ui-md font-medium">Save</button><h1 className="type-h1 font-medium">Softer page title</h1> // Plain text-* still works and now carries leading + tracking<span className="text-sm">13 / 18</span>Sizes are authored in px and emitted in rem against a 16px root. Tracking follows Inter's dynamic metrics.
| Size | Leading | Tracking | Utility | Variables |
|---|---|---|---|---|
| 12px | 16px | 0.001em | text-xs | --font-size-12 · --line-height-16 · --tracking-12 |
| 13px | 18px | -0.003em | text-sm | --font-size-13 · --line-height-18 · --tracking-13 |
| 14px | 20px | -0.006em | text-md | --font-size-14 · --line-height-20 · --tracking-14 |
| 18px | 26px | -0.014em | text-lg | --font-size-18 · --line-height-26 · --tracking-18 |
| 22px | 28px | -0.018em | type-h3 | --font-size-22 · --line-height-28 · --tracking-22 |
| 28px | 34px | -0.021em | type-h2 | --font-size-28 · --line-height-34 · --tracking-28 |
| 36px | 42px | -0.022em | type-h1 | --font-size-36 · --line-height-42 · --tracking-36 |
| 48px | 54px | -0.022em | type-display | --font-size-48 · --line-height-54 · --tracking-48 |
Controls, labels, rows, anything that is not a sentence. Pixel leading so rows stack on a grid, tabular figures so numbers hold their width.
Save changes · 12:45 PM · 1,204 items
type-ui-xs- size
- 12px
- leading
- 16px
- tracking
- 0.001em
Captions, timestamps, badges
Save changes · 12:45 PM · 1,204 items
type-ui-sm- size
- 13px
- leading
- 18px
- tracking
- -0.003em
Default control and body size: labels, list rows, menu items
Save changes · 12:45 PM · 1,204 items
type-ui-md- size
- 14px
- leading
- 20px
- tracking
- -0.006em
Roomier controls and body text
Save changes · 12:45 PM · 1,204 items
type-ui-lg- size
- 18px
- leading
- 26px
- tracking
- -0.014em
Prominent controls, dialog titles
Running text. Same sizes as UI but a unitless 1.5 leading that recomputes for nested sizes, proportional figures, and pretty wrapping.
Meeting notes stay in sync across devices, so the summary you edit on the desktop is the one you share.
type-prose-xs- size
- 12px
- leading
- 1.5
- tracking
- 0.001em
Fine print
Meeting notes stay in sync across devices, so the summary you edit on the desktop is the one you share.
type-prose-sm- size
- 13px
- leading
- 1.5
- tracking
- -0.003em
Dense reading: transcripts, side panels
Meeting notes stay in sync across devices, so the summary you edit on the desktop is the one you share.
type-prose-md- size
- 14px
- leading
- 1.5
- tracking
- -0.006em
Body copy, chat messages, notes
Meeting notes stay in sync across devices, so the summary you edit on the desktop is the one you share.
type-prose-lg- size
- 18px
- leading
- 1.5
- tracking
- -0.014em
Lead paragraphs
Monospace on the same four steps. No tracking, no ligatures, tabular figures.
const summary = await meeting.summarize({ speakers: 3 })
type-code-xs- size
- 12px
- leading
- 16px
- tracking
- 0em
Inline code inside sm and xs text
const summary = await meeting.summarize({ speakers: 3 })
type-code-sm- size
- 13px
- leading
- 18px
- tracking
- 0em
Code blocks, inline code in md text
const summary = await meeting.summarize({ speakers: 3 })
type-code-md- size
- 14px
- leading
- 20px
- tracking
- 0em
Editors
const summary = await meeting.summarize({ speakers: 3 })
type-code-lg- size
- 18px
- leading
- 26px
- tracking
- 0em
Presentation code
Four steps above the text band, each with leading of size + 6, all semibold. Display alone is set in the brand face, Greed Standard, and falls back to Inter where it is not loaded.
Tuesday standup with the design team
type-h3- size
- 22px
- leading
- 28px
- tracking
- -0.018em
- weight
- semibold
Section headings
Tuesday standup with the design team
type-h2- size
- 28px
- leading
- 34px
- tracking
- -0.021em
- weight
- semibold
Page section titles
Tuesday standup with the design team
type-h1- size
- 36px
- leading
- 42px
- tracking
- -0.022em
- weight
- semibold
Page titles
Tuesday standup with the design team
type-display- size
- 48px
- leading
- 54px
- tracking
- -0.022em
- weight
- semibold
Hero and empty-state statements, in the brand face
One TypeScript module is the source of truth. A Tailwind plugin emits the variables and classes; this page imports the same module.
/* packages/ui/src/styles/typography.ts emits three layers */ /* 0. primitives */:root { --font-size-14: 0.875rem; --line-height-20: 1.25rem; --tracking-14: -0.006em; } /* 1. roles */:root { --ui-md-size: var(--font-size-14); --ui-md-leading: var(--line-height-20); } /* 2. classes, in the components layer so any utility overrides them */.type-ui-md { font-size: var(--ui-md-size); line-height: var(--ui-md-leading); letter-spacing: var(--ui-md-tracking); font-variant-numeric: tabular-nums;}