Typography
The fluid type scale, the font stack, Heading/Text/Prose, and tabular numbers for aligned digits.
Last updated
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.
Show codeHide code
<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.
<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:
@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>.
Show codeHide code
<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><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.
<span class="tabular-nums">1,204</span>
This is a plain Tailwind utility, not a token — add it directly wherever digits need to align.