Updated July 10, 2026

Naming Typography Tokens (display, heading, body, caption)

Name typography tokens after the role the text plays — display, heading-lg, body-md, caption — never after their values (font-24) or the HTML elements they style (h2-size). Value names go stale the moment the scale is retuned, and element names couple visual style to document markup, which are two different decisions. A role name states the one thing about the token that is meant to stay true: what job this text does.

The second half of the rule is just as important: a typography token is composite. Size, line height, weight and letter spacing travel together under the role name — a body-md that only carries a size isn’t a style, it’s a number. This article covers both halves, the t-shirt-size convention that keeps role sets small, and the export path that carries the names into CSS, Tailwind and Figma. It’s part of our type scale guide.

Why not name tokens after values?

Because values are the part of the system designed to change. Suppose your scale generates a 24px heading size and you name the token font-24. Next quarter the ratio is nudged from 1.25 to 1.333 and that step now computes to 25px. Your options are both bad: rename the token everywhere it’s used — a breaking change across every stylesheet, component and design file that references it — or keep a token called font-24 whose value is 25, which is worse, because now the name actively lies.

A role name survives the retune untouched. heading-md can move from 24px to 25px and every usage remains correct, because the name never claimed a value — it claimed a job, and the job didn’t change. This is the same reasoning that leads color systems away from names like blue-500 for semantic slots: name the decision, not the current answer.

Why not name tokens after elements?

h2-size looks harmless — until style and markup need to disagree, which in real products is constantly. Heading levels in HTML express document structure: an h2 is “a section one level below an h1”, a fact that screen readers and outlines rely on. Visual rank is a separate decision. A card title might be an h3 on the dashboard and an h2 on its own page while looking identical in both places; a legal page might carry six levels of structure but only three levels of visual differentiation.

Element-named tokens force a choice between two failure modes: pick the element for its looks (wrong outline, an accessibility cost) or pick it for structure and get the wrong style. Role-named tokens dissolve the conflict — any element can wear heading-md, and the document outline stays an independent, semantic decision.

What does a typography token actually contain?

A complete text style. The properties of a text style are tuned as a unit: a 28px heading gets its 36px line height because it’s 28px, its 600 weight because headings rank by darkness, its −0.01em letter spacing because built-in font spacing reads loose as sizes grow. Apply the size without the rest and you haven’t applied the style — you’ve applied a fragment and left the other three properties to whatever the context happens to inherit.

This is why the DTCG token format defines typography as a composite type: one token whose value is the whole bundle — family, size, weight, line height, letter spacing — rather than five loose tokens hoping to be used together.

How does the t-shirt-within-role convention work?

Most systems settle on a two-part name: the role family first, a t-shirt size within the role second — body-sm, body-md, body-lg. The families are few and stable: display at the top, a run of heading sizes, a run of body sizes, and the small utility roles — caption and overline — at the bottom. The t-shirt suffix absorbs growth: when a design genuinely needs a larger body style, body-lg appears without renaming anything that exists.

Two conventions keep the sets honest. Roles that occur once don’t get a suffix — caption rather than caption-md — until a second variant actually exists. And display sits above the heading run as its own family rather than as heading-xl, because its job (a hero statement read in one glance) differs from a heading’s job (wayfinding), and its properties — tighter line height, tighter tracking — break the heading pattern rather than extend it.

What does a full role set look like?

Here is a worked set, generated from a 16px base at ratio 1.333 with two notes per interval, with line heights snapped to the 8px baseline (half the base) and font family omitted for brevity:

{
  "type": {
    "display":    { "$type": "typography", "$value":
      { "fontSize": "51px", "lineHeight": "56px", "fontWeight": 700, "letterSpacing": "-0.02em" } },
    "heading-lg": { "$type": "typography", "$value":
      { "fontSize": "38px", "lineHeight": "48px", "fontWeight": 700, "letterSpacing": "-0.015em" } },
    "heading-md": { "$type": "typography", "$value":
      { "fontSize": "28px", "lineHeight": "36px", "fontWeight": 600, "letterSpacing": "-0.01em" } },
    "heading-sm": { "$type": "typography", "$value":
      { "fontSize": "21px", "lineHeight": "28px", "fontWeight": 600, "letterSpacing": "0em" } },
    "body-lg":    { "$type": "typography", "$value":
      { "fontSize": "18px", "lineHeight": "28px", "fontWeight": 400, "letterSpacing": "0em" } },
    "body-md":    { "$type": "typography", "$value":
      { "fontSize": "16px", "lineHeight": "24px", "fontWeight": 400, "letterSpacing": "0em" } },
    "body-sm":    { "$type": "typography", "$value":
      { "fontSize": "14px", "lineHeight": "20px", "fontWeight": 400, "letterSpacing": "0em" } },
    "caption":    { "$type": "typography", "$value":
      { "fontSize": "12px", "lineHeight": "16px", "fontWeight": 400, "letterSpacing": "0.02em" } },
    "overline":   { "$type": "typography", "$value":
      { "fontSize": "12px", "lineHeight": "16px", "fontWeight": 500, "letterSpacing": "0.08em" } }
  }
}

Every size sits on the scale, every line height is a multiple of the 8px baseline, and each token reads as a complete style — the reviewer can see at a glance that small sizes gain weight and tracking while large sizes shed both.

This is the shape Scale Composer assembles as you assign scale steps to roles: open the token export for this scale — the semantic levels from caption to display, each gathered with its size, line height, weight and letter spacing under its role name.

Scale Composer's token export showing a full typography role set from caption to display as named composite tokens

How do role tokens travel into code and design tools?

The role names are the contract; the formats are transports. From one DTCG source the same set exports as CSS custom properties, as a Tailwind v4 @theme block, and as Figma Variables — so the body-md a designer applies in Figma and the body-md a developer references in a stylesheet are the same token, not two conventions that happen to rhyme. That shared vocabulary is the real payoff of role naming: “make it heading-sm” is an instruction both sides can execute without a translation table, and a scale retune propagates to every surface by regenerating the exports rather than renaming anything.

Name your own set

Naming is easiest done while the values are still live: open the same role set as Tailwind v4 @theme output, rename a level, and check the instinct that matters — would this name still be true if every pixel value in the file changed tomorrow? If yes, it’s a role. If no, it’s a value wearing a name tag.

Keep reading

  • What Is a Modular Type Scale?

    A modular type scale generates every font size from one base size and one ratio. Learn the formula, how to pick a ratio, and why scales beat hand-picked sizes.

  • How Many Font Sizes Does a Design System Need?

    How many font sizes does a design system need? Why roles decide the count, why many systems land near 9–12 levels, and when adding a size is justified.