
# Type Scale

A brand charter's type section is a ladder: display, then the heading levels, then body, then the small print. Each rung is a bound triple — a size, the leading that goes with it, and the weight it is set in. `design.typeScale` is where that ladder lives.

```yaml
design:
  typeScale:
    h1:
      size: '3rem'
      lineHeight: 1.1
      weight: 700
      letterSpacing: '-0.02em'
    body:
      size: '1rem'
      lineHeight: 1.6
    caption:
      size: '0.8125rem'
      lineHeight: 1.4
```

Every step is optional. An app that declares only `h1` and `body` has a real, if short, scale — the key is meant to be adopted a rung at a time.

## The twelve steps

The set is closed, and the order below is the order the ladder is published in — largest first — whatever order you wrote the keys in.

| Step        | What it is                                                        |
| ----------- | ----------------------------------------------------------------- |
| `display`   | The one-off page-opening size, above `h1`.                        |
| `h1`        | Page title. One per page.                                         |
| `h2`        | Section heading.                                                  |
| `h3`        | Sub-section heading.                                              |
| `h4`        | Fourth-level heading.                                             |
| `h5`        | Fifth-level heading.                                              |
| `h6`        | Sixth-level heading.                                              |
| `lead`      | The standfirst paragraph that opens a page, set larger than body. |
| `body`      | Running text. The step every other one is measured against.       |
| `bodySmall` | Secondary running text: help text, dense tables.                  |
| `caption`   | Labels, timestamps, footnotes.                                    |
| `overline`  | The small tracked-out eyebrow above a heading.                    |

A step name outside this list is refused by name at validation — `h7` is reported as an unknown key rather than silently ignored. `h1` through `h6` are deliberately the same names the [`text` component](/en/docs/content-components) already emits as `element:`, so a step is something a renderer can bind to rather than a string it has to guess at.

## What a step holds

| Member          | Type     | Notes                                                                           |
| --------------- | -------- | ------------------------------------------------------------------------------- |
| `size`          | `string` | **Required.** A number with `px` or `rem` — `'3rem'`, `'14px'`.                 |
| `lineHeight`    | `number` | A **unitless ratio**: `1.5`, not `'1.5'` and not `'24px'`.                      |
| `weight`        | `number` | 100–900, the same ladder `theme.fonts` uses.                                    |
| `letterSpacing` | `string` | A number with `px`, `rem` or `em` — `'-0.02em'`.                                |
| `font`          | `string` | The name of a face declared in `design.theme.fonts` — `'title'`, not `'Inter'`. |

`size` is the only required member, because a step declaring nothing but a size is a real and common state — the leading and weight inherit. A step declaring leading and no size names no size at all and cannot render.

`font` names a face rather than a family so the binding survives a retune: a step reading `font: title` follows the title face wherever you change it, whereas an inlined `'Inter'` is a copy that goes stale silently. It is cross-checked against your declared faces, so an unresolvable name is refused rather than emitted as a dangling variable.

```yaml
design:
  theme:
    fonts:
      title:
        family: Inter
        fallback: 'system-ui, sans-serif'
  typeScale:
    display:
      size: '4.5rem'
      lineHeight: 1.05
      weight: 800
      font: title
```

## What it emits

Every declared step becomes a Tailwind type token: one custom property for the size and one modifier for each optional member.

```css
:root {
  --text-h1: 3rem;
  --text-h1--line-height: 1.1;
  --text-h1--font-weight: 700;
  --text-h1--letter-spacing: -0.02em;
  --text-h1--font-family: var(--font-title);
}
```

The variables reach `:root` unconditionally, so anything can read `var(--text-h1)`. The matching `text-h1` **utility** is generated the way every Tailwind utility is — when the class name appears in a `className` the build-time scan can see:

```yaml
pages:
  - path: /
    components:
      - type: text
        element: h1
        props:
          className: text-h1
        content: Charter
```

Applying the utility sets the font size and folds in the leading, weight, tracking and face in one class. That is the whole point of the key: a step you can use, not a variable nobody can reach.

## Why the units are what they are

Each constraint tracks the [W3C Design Tokens](https://www.designtokens.org/tr/drafts/format/) type the step serialises to, because a value the export cannot carry faithfully is a value the charter cannot publish.

- **`size` takes `px` or `rem` and nothing else.** Those are the two units a DTCG `dimension` permits. A fluid `clamp(1rem, 2vw, 3rem)` is refused rather than quietly accepted: fluid type is a layout technique for the one element that needs it, whereas a type scale is a ladder of fixed, quotable steps — "our h1 is 3rem" is the kind of sentence a charter exists to make true. Reach for a utility class on that element instead.
- **`lineHeight` is a number, not a string.** DTCG types it as a number, and the better practice agrees independently: a ratio survives a size change, whereas a fixed `24px` leading silently becomes wrong the moment the step is retuned.
- **`letterSpacing` additionally takes `em`, which DTCG does not.** `-0.02em` is the idiomatic tracking value in essentially every type scale ever written, and refusing it to satisfy a serialisation format would be the format dictating the design. It is honoured in the browser verbatim, and named in the export's `unmappable` list at its exact config path — so you can see that Sovrium rendered it and could not carry it in the token document.

## What it supersedes

`design.typeScale` replaces three `theme.fonts.*` fields that validate and then reach nothing:

| Superseded                 | What it actually did                                                                                                |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `theme.fonts.*.lineHeight` | Nothing at all. No custom property was ever emitted for it.                                                         |
| `theme.fonts.*.size`       | Reached only the legacy `hero` section renderer, as an inline size. Never a CSS variable.                           |
| `theme.fonts.*.weights`    | Only `weights[0]` was read, by that same renderer. Never an `@font-face`, so the extra entries loaded no font file. |

**Superseded, not deprecated** — the distinction is worth keeping. "Deprecated" means _this worked and is going away_; these three never worked. They keep decoding until the next major, alongside the top-level `theme` alias, because refusing them now would stop a booting app in exchange for zero rendering change. `sovrium validate` prints a `Superseded:` notice naming each declared path and exits `0`.

They are not translated for you. The shapes genuinely differ — `typeScale` is per **step** where `theme.fonts` is per **face**, its `lineHeight` is a ratio where the old one was a free string, and it takes one `weight` where the old one took an array — so any automatic mapping would have to guess which face is which rung of the ladder. Sovrium says so instead of guessing.

## In the export

`sovrium design-system` and the two admin export endpoints publish the ladder as a **Type scale** section, in the canonical order above. In the DTCG document each step is a `typography` composite token, with `letterSpacing` values in `em` listed under `unmappable` for the reason given above.

## Related Pages

- [Design System](/en/docs/design) — the `design` key this belongs to, and the export.
- [Typography](/en/docs/theme-typography) — `theme.fonts`, the faces a step is set in.
- [Theme Overview & Colors](/en/docs/theme) — the other token categories.
- [Content Components](/en/docs/content-components) — the `text` component a step is applied to.
- [Design System Console](/en/docs/design-system-console) — the ladder drawn at its rendered sizes.
