# Typography

> The fluid type scale, the font stack, Heading/Text/Prose, and tabular numbers for aligned digits.

URL: https://apogee.avaui.dev/docs/typography
Last updated: 2026-10-15

The fluid type scale, the font stack, Heading/Text/Prose, and tabular numbers for aligned digits.

Last updated October 15, 2026

One fluid type scale, exposed through two components: `Heading` for display/heading sizes, `Text` for body copy. Both read token-backed Tailwind utilities — no hard-coded font sizes in markup.

## Type scale

`--text-display` and `--text-h1` … `--text-h6` are `clamp()` values (min at a ~320px viewport, max at ~1440px), each paired with its own `--*--line-height`. Tailwind’s `--text-*` namespace turns them into utilities (`text-display`, `text-h1` … `text-h6`) with the line-height applied automatically.

| Token | Value | Line height |
| --- | --- | --- |
| `--text-display` | `clamp(2.5rem, 1.6rem + 4.5vw, 4.5rem)` | `1.05` |
| `--text-h1` | `clamp(2rem, 1.5rem + 2.5vw, 3rem)` | `1.1` |
| `--text-h2` | `clamp(1.625rem, 1.35rem + 1.4vw, 2.25rem)` | `1.15` |
| `--text-h3` | `clamp(1.375rem, 1.2rem + 0.9vw, 1.75rem)` | `1.2` |
| `--text-h4` | `clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem)` | `1.3` |
| `--text-h5` | `clamp(1rem, 0.97rem + 0.2vw, 1.125rem)` | `1.4` |
| `--text-h6` | `clamp(0.875rem, 0.85rem + 0.15vw, 1rem)` | `1.45` |

No breakpoint jumps — each size interpolates smoothly between the two anchors.

## Heading

`as` sets the semantic tag (`h1`–`h6`, or `p`/`div`); `size` sets the visual size independently, defaulting to `as` when it’s a heading. Keep heading order correct for accessibility even when the visual size differs from the level.

The demo below uses `as="div"` for every step so the page doesn’t end up with seven `<h1>`s — in real content, `as` should match your outline.

```astro
<Heading as="div" size="display">Display</Heading>
<Heading as="div" size="h1">Heading 1</Heading>
<Heading as="div" size="h2">Heading 2</Heading>
<Heading as="div" size="h3">Heading 3</Heading>
<Heading as="div" size="h4">Heading 4</Heading>
<Heading as="div" size="h5">Heading 5</Heading>
<Heading as="div" size="h6">Heading 6</Heading>
```

Every `Heading` also gets `text-balance` and `tracking-tight`.

## Text

Body copy. Unlike `Heading`, `Text` sizes (`small` / `medium` / `large`, default `medium`) are Tailwind’s own static scale, not the fluid tokens above — body text doesn’t need to fluid-resize the way display copy does. `leading-relaxed` by default; `color="neutral-faded"` switches to the secondary text colour.

```astro
<Text>Default body copy.</Text>
<Text size="small" color="neutral-faded">Secondary, smaller copy.</Text>
```

## Font stack

`theme.css` defines three font tokens in `@theme`, defaulting to the platform system stacks (so text looks native out of the box):

| Token | Used for |
| --- | --- |
| `--font-sans` | Body / UI text (the `font-sans` utility and the default body font) |
| `--font-mono` | Code (`font-mono`, `Code`, `Kbd`) |
| `--font-serif` | Quotations (`font-serif`, `Blockquote`, prose blockquotes) |

To brand the type, override `--font-sans` (or `--font-mono`) in your own `@theme` block — same one-place pattern as the color tokens, no component changes:

```css
@theme {
  --font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
}
```

## Prose

`Heading` and `Text` style elements you control one at a time. `Prose` is for the opposite case — a block of rich or CMS HTML you don’t touch element-by-element (`set:html={article.body}` or Markdown output). It’s a scoped, token-driven `.apogee-prose` stylesheet (escape-hatch CSS, since Tailwind utilities can’t reach injected HTML) covering headings, links, lists, blockquotes, code, and `<hr>`.

```astro
<Prose>
<h2>Release notes</h2>
<p>Body copy with a <a href="#">link</a> and <strong>bold</strong> text.</p>
<ul>
  <li>First item</li>
  <li>Second item</li>
</ul>
</Prose>
```

```astro
<Prose set:html={article.body} />
```

## Tabular numbers

Any component whose job is to show numbers that line up — tables, pagination, stats, prices, counts, timers — should use `tabular-nums` so digits share a fixed width and don’t jitter as they change.

```astro
<span class="tabular-nums">1,204</span>
```

This is a plain Tailwind utility, not a token — add it directly wherever digits need to align.
