Design Tokens — typography

Typography sets the product’s tone more than many “visible” decorative choices. In a design system it must be a scale and a contract: families, sizes, weights, line-height, letter-spacing, and semantic roles (h1, body, caption).

Core typography axes

  1. Font family — brand character and readability.
  2. Font size — hierarchy and interface density.
  3. Font weight — emphasis without overusing bold.
  4. Line-height — line rhythm and paragraph readability.
  5. Letter-spacing — headings, uppercase, compact labels.
  6. Type scale — a regular size system, not a random set of px values.

Choosing families and pairing

System stack

Maximum performance, zero download, familiar OS look. Con — weaker brand uniqueness.

Web fonts (for example, Inter + Fira Code)

A balance of control and speed. You need font-display, a sensible set of weights, and fallbacks.

Custom / self-hosted

Maximum brand and subsetting control, but licenses, infrastructure, and bundle size are your responsibility.

A practical pairing for product UI:

  • Sans for the interface (Inter / system-ui);
  • Mono for code and technical values;
  • optionally Display/Serif only for marketing headlines, not dense app screens.

Do not mix four decorative families “for beauty” — usually 1–2 are enough.

Type scale

Recommended body base — 16px (1rem). Then a modular scale:

export const fontSize = {
  xs: '0.75rem',    // 12px
  sm: '0.875rem',   // 14px
  base: '1rem',     // 16px
  lg: '1.125rem',   // 18px
  xl: '1.25rem',    // 20px
  '2xl': '1.5rem',  // 24px
  '3xl': '1.875rem',// 30px
  '4xl': '2.25rem', // 36px
  '5xl': '3rem'     // 48px
};
export const fontWeight = {
  normal: 400,
  medium: 500,
  semibold: 600,
  bold: 700
};

export const lineHeight = {
  tight: 1.25,
  normal: 1.5,
  relaxed: 1.75
};

Semantic typography

Sizes alone are not yet a system. You need roles:

export const text = {
  h1: { size: '4xl', weight: 'bold', leading: 'tight', tracking: 'tight' },
  h2: { size: '3xl', weight: 'bold', leading: 'tight' },
  h3: { size: '2xl', weight: 'semibold', leading: 'tight' },
  body: { size: 'base', weight: 'normal', leading: 'normal' },
  caption: { size: 'xs', weight: 'normal', leading: 'normal' }
};

Then designer and engineer speak the same language: “this is body,” not “this is roughly 15–16px.”

Line-height and letter-spacing

  • Headings: tighter leading (about 1.1–1.3), often slightly negative tracking.
  • Body: 1.4–1.6.
  • Long-form: closer to 1.6–1.8.
  • Uppercase labels: positive letter-spacing, otherwise they “stick together.”

Responsive typography

Two workable approaches:

  1. Fluid via clamp() for heroes and headings.
  2. Breakpoint-based tokens for UI where predictability matters.
.heading-hero {
  font-size: clamp(2rem, 4vw + 1rem, 3rem);
  line-height: 1.15;
}

Do not make the whole interface “rubbery” without need: forms and tables often work better on a discrete scale.

Accessibility

  • Body no smaller than 16px without a strong reason.
  • Text should scale to 200% without breaking layout.
  • Use rem/em for interface sizes.
  • Text contrast follows the same rules as color tokens.
  • Do not convey meaning only through light weight on gray.
Important

Typography tokens matter for a11y as much as color tokens: tiny low-contrast text “looks nice” in a mock and works poorly for real users.

Lesson takeaway

A good typography system = limited pairing + regular scale + semantic roles + checked line-height/tracking + responsive rules. That is the readability foundation of the whole DS.