Typography
Six semantic text roles — display, heading, body, action, label, caption — 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.
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 shrink at S; body, action, label, caption 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.
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).
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.
Action
Interactive elements — buttons, tabs, links, breadcrumbs. Weight medium. Letter-spacing slightly-wide (action-s widens further at +0.02em). Action is the role designed to swap face. It's the only role pointing at font.family.2 by default — change slot 2 in the theme editor and every button/tab/link label switches typography while body stays unchanged.
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.
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.
What's next — phase 2
Phase 1 (shipped 2026-05-13) added the architectural plumbing: numbered family slots, per-role family aliases, S/M/L responsive modes. Sizes stayed hand-tuned. Phase 2 will bring the editor UI: Google Fonts picker with lazy-load, per-mode base-size sliders, role-to-slot map editor, and (optionally) a formula-driven scale mode where sizes derive from base × ratio^n. Tracked in tokens/PLUGIN-HANDOFF.md.