Building a Typography System With Design Tokens
Font size alone isn't a type system. A walk through the token structure I use to make typography consistent across platforms.
Typography is the hardest part of a design system to get right — and the first thing users notice when it's wrong.
What a type system actually is
A type system isn't a list of font sizes. It's a set of semantic decisions: what is a heading, what is body copy, what is a caption — and how do those things scale across breakpoints and platforms.
Good type tokens separate three concerns:
- Primitive tokens: raw values (font-size-16, font-weight-600)
- Semantic tokens: meaning-bearing aliases (text-body, text-heading-lg)
- Component tokens: scoped to a component (button-label-size)
The token hierarchy I use
// Primitive
--font-size-xs: 0.75rem;
--font-size-sm: 0.875rem;
--font-size-base: 1rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 1.875rem;
--font-size-4xl: 2.25rem;
// Semantic
--text-body: var(--font-size-base);
--text-body-sm: var(--font-size-sm);
--text-heading-sm: var(--font-size-xl);
--text-heading-md: var(--font-size-2xl);
--text-heading-lg: var(--font-size-3xl);
--text-display: var(--font-size-4xl);The key rule: no primitive tokens in components
Components should only ever reference semantic tokens. This way you can update the scale — or swap the entire type system — by editing a single layer.
Pair this with a line-height and letter-spacing token for each semantic tier, and you have a system that's genuinely portable across web, React Native, and Figma variables.