Typography

Seven semantic text roles — display, heading, body, action, label, caption, code — composed from a three-link token chain across L1 and L2. L1 font-family slots (numbered 1/2/3) hold the actual face. L2 family aliases (one per role) map each role to a slot. L2 composite tokens bake font-family + size + weight + line-height + letter-spacing into a single style. The brand swaps a slot's value and every role bound to it changes face — without touching any composite.


Architecture

Three layers move together. Edit a slot at L1, every L2 role bound to it changes. Edit a role's family alias at L2, only that role swaps. Edit a composite directly only when a one-off needs to deviate (it shouldn't — that's an L3 component concern). The slot each role points at is set by the L2 family aliases in tokens/source-tokens.json; the map below shows the shipped mapping.

Role (L2)
Family alias
Slot (L1) → font
display
typography.family.display
Slot 2 —
heading
typography.family.heading
Slot 2 —
body
typography.family.body
Slot 1 —
action
typography.family.action
Slot 1 —
label
typography.family.label
Slot 1 —
caption
typography.family.caption
Slot 1 —
code
typography.family.code
Slot 3 —

Overline has no family alias of its own — it follows label. The code role also styles monospace micro-UI (timestamps, hex inputs, calendar times), so remapping it moves those too.

Responsive modes — S / M / L

Every L2 typography token declares three modes: S (mobile, ≤600px), M (tablet, 601–1023px), L (desktop, ≥1024px). Display and heading (xl/l/m) shrink at S; body, action, label, caption, overline and code stay constant across all three. Empty $modes blocks mean "inherit base" — they exist in the JSON so the Figma plugin can build variable modes from them.

Preview a mode without resizing: open the Theme editor (cog icon in the header) — the Viewport switcher at the top has Auto / S / M / L buttons. Auto follows your real viewport; the others force a specific mode for visual review.

Display

The biggest type in the system — for hero sections, marketing moments, and impactful brand statements. Five sizes from xl (largest) to xs. Weight bold. All five shrink at S to keep mobile readable.

display-xl
Display XL
display-l
Display L
display-m
Display M
display-s
Display S
display-xs
Display XS

Heading

UI hierarchy headings for sections, cards, modals. Four sizes xl to s. Weight semibold (one step less weight than display). Heading-xl, l, and m shrink at S; heading-s stays fixed (already small enough for mobile).

heading-xl
Heading XL
heading-l
Heading L
heading-m
Heading M
heading-s
Heading S
heading-xs
Heading XS — card titles, dense panels
heading-2xs
Heading 2XS — sidebar groups, table headers

Body

Running text for paragraphs and prose. Five sizes xl to xs. Weight regular. Letter-spacing normal. Same values at every breakpoint — body never shrinks below readable.

body-xl
The quick brown fox jumps over the lazy dog
body-l
The quick brown fox jumps over the lazy dog
body-m
The quick brown fox jumps over the lazy dog
body-s
The quick brown fox jumps over the lazy dog
body-xs
The quick brown fox jumps over the lazy dog

Action

Interactive elements — buttons, tabs, links, breadcrumbs. Weight medium. Letter-spacing slightly-wide (action-s widens further at +0.02em). Action shares the text face (font.family.1) with body by default — the norm across brands and platforms, where buttons follow the body font and differentiate by weight and spacing. A brand that wants a distinct UI voice remaps the action role to its own slot in the theme editor's role map; the demo below previews what an action-only face swap looks like.

Try it Only action specimens swap. Body stays Inter — proves per-role mapping works.
action-l
Action L — Buttons, Links, Tabs
action-m
Action M — Buttons, Links, Tabs
action-s
Action S — Buttons, Links, Tabs

Label

Static UI labels — form field labels, tags, chip text, badges. Same metrics as action (weight medium, slightly-wide letter-spacing) but semantically different: labels aren't interactive. Bound to slot 1 by default.

label-l
Form field label
label-m
Form field label
label-s
Form field label

Caption

Helper text, metadata, fine print, image captions, timestamps. Two sizes m and s. Weight regular. Smaller than body-xs (caption-s = 11px) so it reads as supplementary, not primary.

caption-m
Caption M — helper text, metadata
caption-s
Caption S — helper text, metadata

Overline

Uppercase eyebrow above a heading — section markers, kickers, card categories. One size. Family follows the label role.

overline
Overline — section eyebrow

Code

Monospace text — code blocks, inline code, and the monospace micro-UI (timestamps, hex inputs, calendar times). Three sizes. The only role bound to slot 3 by default.

code-l
const theme = tokens.load()
code-m
const theme = tokens.load()
code-s
const theme = tokens.load()

Retune it live

Everything on this page is editable in the Theme Editor (header button) → Typo: a Google Fonts picker per family slot, the role map that decides which slot each role wears (the slot captions update live as you remap), and per-mode base-size sliders that scale the whole ramp. Export carries the result as standard DTCG JSON; the letbe tokens plugin registers the same slots, roles and modes as Figma variables.