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.

Display
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Show 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>.

Release notes

Body copy with a link and bold text.

  • First item
  • Second item
Show 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.