# Typography

Font families, the type scale and six independent text roles: heading, body, prose, UI, metadata and code.

Each role owns its size, weight, line height and letter spacing, so a role can change without affecting the others. Line heights are unitless ratios and letter spacing is relative to the element's font size. Native elements take their role without a class; the role classes, `.formatted` for authored text and the reading classes are listed under [Classes](#classes); they set presentation only and do not create semantics, render Markdown or sanitize HTML.

## Font families

Serif supplies editorial headings and authored prose, sans-serif supports reading, and monospace serves interface text, metadata and code. A family alone does not define a role: when a component needs only the monospace family, it sets `font-family: var(--font-mono)` and keeps inheriting the other text properties.

| Token          | Role                                                      |
| -------------- | --------------------------------------------------------- |
| `--font-serif` | Editorial headings, large page titles and authored prose. |
| `--font-sans`  | Paragraphs and reading text.                              |
| `--font-mono`  | Interface, navigation, metadata and code.                 |

`base.css` declares every font face, self-hosted through `@dg/ui/fonts/*`; no font service is contacted. Teodor is a variable font whose italics use its slant axis, capped at 10°. Roboto and Roboto Mono are variable Latin subsets; other characters fall back to the rest of each stack.

## Type scale

Every type size derives from `--font-size-base`, which defaults slightly below the browser root size and follows it. Change this one length to scale every role; spacing does not follow it. Body text uses the base itself, and every other role and heading level its own factor. A shared step is added only when several roles share a size.

| Token              | Role                                                                          |
| ------------------ | ----------------------------------------------------------------------------- |
| `--font-size-base` | Reference length for the entire typography scale; spacing does not follow it. |

To scale every role inside one element, add `.typescale` where its local `--font-size-base` is set; otherwise descendants keep the sizes computed from the root base. Use `rem` or `px`, or register `--font-size-base` as a `<length>` with `@property` for a container-relative base.

```html
<section class="typescale" style="--font-size-base: 1.5rem">
  <h1>A larger presentation</h1>
  <p>All type roles follow the same base.</p>
  <p class="metadata">A proportionally sized reference</p>
</section>
```

## Heading role

Native h1–h6 headings and `Heading` use the serif family in `--text-prominent`, set light and tight, without margins. H1 grows with the viewport between bounds proportional to the base where typed division is supported, with a proportional fallback elsewhere; h2–h6 are fixed multiples of the base. Choose heading levels for document hierarchy; an appearance override does not change that hierarchy.

| Token                      | Role                                                                   |
| -------------------------- | ---------------------------------------------------------------------- |
| `--heading-1-font-size`    | Default h1 size; fluid with the viewport between root-relative bounds. |
| `--heading-2-font-size`    | Default h2 size in the shared heading scale.                           |
| `--heading-3-font-size`    | Default h3 size in the shared heading scale.                           |
| `--heading-4-font-size`    | Default h4 size in the shared heading scale.                           |
| `--heading-5-font-size`    | Default h5 size in the shared heading scale.                           |
| `--heading-6-font-size`    | Default h6 size in the shared heading scale.                           |
| `--heading-font-weight`    | Default HTML headings.                                                 |
| `--heading-line-height`    | Default HTML headings.                                                 |
| `--heading-letter-spacing` | Editorial headings, set tight.                                         |

## Body role

The document default, inherited by every element that sets no other role: paragraphs, descriptions and reading text in the sans-serif family. It needs no class. To change the body size on one scope, set `--body-font-size` and also `font-size: var(--body-font-size)` there, so descendants inherit it.

| Token                   | Role                                         |
| ----------------------- | -------------------------------------------- |
| `--body-font-size`      | Reading text and descriptions.               |
| `--body-font-weight`    | Paragraphs and reading text.                 |
| `--body-line-height`    | Document default, inherited by reading text. |
| `--body-letter-spacing` | Reading text and descriptions.               |

## Prose role

`.prose` sets continuous authored writing, such as a presentation or a text about an artwork, in the serif family, slightly above the body size to offset the serif's smaller x-height. Interface descriptions, captions and short reading text keep the body role. Combine it with `.formatted` for structured HTML and with `.constrained-width` for the reading measure.

| Token                    | Role                                                         |
| ------------------------ | ------------------------------------------------------------ |
| `--prose-font-size`      | Continuous authored writing in the serif family.             |
| `--prose-font-weight`    | Continuous authored writing.                                 |
| `--prose-line-height`    | Continuous authored writing, independent of the body rhythm. |
| `--prose-letter-spacing` | Continuous authored writing.                                 |

```html
<div class="formatted prose constrained-width">
  <p>A text written by the artist about the work.</p>
</div>
```

## UI role

`.ui` sets navigation, actions and interface labels in uppercase monospace. Components apply it to their controls; native `label`, `legend`, `select` and `thead` take it without the class. Uppercase is visual: source strings, and what screen readers announce, stay unchanged. Scope the class to interface elements: formatted content inside a `.ui` parent still inherits its typography.

| Token                 | Role                                            |
| --------------------- | ----------------------------------------------- |
| `--ui-font-size`      | Navigation, controls and interface labels.      |
| `--ui-font-weight`    | Navigation, controls and labels.                |
| `--ui-line-height`    | Interface text, independent of the body rhythm. |
| `--ui-letter-spacing` | Navigation, controls and interface labels.      |

```html
<nav class="ui">Collection</nav>
```

## Metadata role

`.metadata` sets dates, authors, sources and references in uppercase monospace with muted color, slightly smaller than interface text and with wider letter spacing. It does not require a UI parent; do not combine it with `.ui`. Keep URLs, titles of works and any text whose casing matters in body text. Form help, validation errors and status messages share this role through [`.feedback`](native-forms.md#native-fields-and-feedback).

| Token                       | Role                                                         |
| --------------------------- | ------------------------------------------------------------ |
| `--metadata-font-size`      | Dates, authors, sources and references accompanying content. |
| `--metadata-font-weight`    | Dates, authors, sources and references.                      |
| `--metadata-line-height`    | Content references, independent of the interface rhythm.     |
| `--metadata-letter-spacing` | Dates, authors, sources and references.                      |

```html
<p class="metadata">Collection · September 2026</p>
```

## Code role

`.code` sets code blocks and literal data, such as token names and values, in monospace at a reduced size. Unlike the UI and metadata roles, it keeps the source casing and slightly tightens its tracking. Every `pre`, `code`, `kbd` and `samp` takes the role's family, weight and tracking, upright and in source casing, even inside `.ui` or `.metadata`. `pre` blocks also take its size and line height and scroll long lines inside the block; inline literals keep the size and line rhythm of their sentence.

| Token                   | Role                                                           |
| ----------------------- | -------------------------------------------------------------- |
| `--code-font-size`      | Code blocks and literal data, such as token names and values.  |
| `--code-font-weight`    | Code blocks and literal data.                                  |
| `--code-line-height`    | Code blocks and data displays, independent of the body rhythm. |
| `--code-letter-spacing` | Slightly tightens Roboto Mono's fixed advance.                 |

```html
<span class="code">--background: oklch(100% 0 0)</span>
```

## Strong emphasis

Strong emphasis raises the weight of text inside any role and keeps its family, size and casing. It marks importance and the keys of structured content, not interactive states: a heavier current or hovered item would change width and move its neighbors.

| Token                  | Role                                                   |
| ---------------------- | ------------------------------------------------------ |
| `--strong-font-weight` | Strong importance, table headers and definition terms. |

Every `strong`, `b`, `th` and `dt` takes this weight, inside or outside `.formatted`; `th` and `dt` also use `--text-prominent`. A role class on the element itself sets that role's weight instead, so give the role to an ancestor, as `thead` does with the interface role. Components may use the token for emphasis without a semantic element.

```html
<p>Load <strong>one</strong> stylesheet in the root layout.</p>
```

## Editorial content

`.formatted` holds authored text: content whose structure comes from its author rather than from the interface, such as rendered Markdown, documentation, a text about an artwork or a chat answer, even a single paragraph. It inherits the typography of its role and adds the rhythm between blocks and the marks that only reading needs. Interface elements, such as navigation, cards, metadata lines and forms, never take it, even when their markup uses paragraphs or lists; [cascade layers](document.md#cascade-layers) describe this division.

Formatted blocks are separated by [`--content-gap-block`](spacing.md#functional-spacing); a following h2–h6 or `hr`, and the block after an `hr`, take one and a half times this gap, so the hierarchy follows an override. Lists and definitions indent on the spacing scale, with muted crosses or numbers; quotations and code blocks use content padding, and code blocks sit on `--background-subtle`. Long words and URLs wrap at any character; for hyphenated breaks, add `hyphens: auto` in the owning styles. Do not make formatted content or its paragraphs a stack or row: block margins and gaps would add up.

The reading classes work with or without `.formatted`. `.constrained-width` limits the line length; to center a constrained block, use the parent stack's `items-center`, and keep text alignment in local CSS. `.underlined` marks a link, or a button presented as a text link, that color alone would not distinguish, since outside formatted content links are identified by their position. `.scrollable` contains a table wider than its column.

| Token                 | Role                                                                      |
| --------------------- | ------------------------------------------------------------------------- |
| `--reading-max-width` | Maximum line length of `.constrained-width`; ch follows the current font. |

## Classes

| Name                 | Description                                                                                                                                                                                                                                       |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `.prose`             | Complete typography for continuous authored writing: serif family and independent prose size, weight, line height and tracking. Inline code, kbd and samp are reduced once to `0.85em`; nested literals inherit it.                               |
| `.ui`                | Complete interface typography: mono family, UI size, weight, line height and tracking, normal style and uppercase. Inherits color.                                                                                                                |
| `.metadata`          | Complete typography for dates, authors, sources and references: mono family, independent metadata tokens, normal style, uppercase and muted color.                                                                                                |
| `.code`              | Complete typography for code blocks and literal data: mono family, independent code tokens, normal style and source casing.                                                                                                                       |
| `.formatted`         | Formats authored text: block spacing, list markers and indents, definition spacing, underlined links, quotation bars, figure caption spacing and code block surfaces. Inherits typography and fills its column; adds no padding or maximum width. |
| `.constrained-width` | Limits the line length to `--reading-max-width` and fills the available width up to it, so a centering parent keeps the measure.                                                                                                                  |
| `.underlined`        | Underlines text like the links of `.formatted`: a 1px line, offset in proportion to the text. Inherits color.                                                                                                                                     |
| `.scrollable`        | Scrolls content wider than its column, such as a wide table, horizontally inside the column. Give the element `tabindex="0"`, `role="region"` and an accessible name so keyboard users can scroll it.                                             |
| `.typescale`         | Recomputes every role size from the local `--font-size-base` and applies the body size.                                                                                                                                                           |

## Usage

```html
<div class="stack items-center">
  <article class="formatted constrained-width">
    <h2>About the work</h2>
    <p class="metadata">Artist name · 2026</p>
    <p>A first paragraph introducing the work.</p>
    <p>A second paragraph with a <a href="/artworks">collection link</a>.</p>
  </article>
</div>
<nav class="row control-gap ui" aria-label="Collection views">
  <a href="/artworks">Artworks</a>
  <a href="/about">About</a>
</nav>
```
