Skip to content

Fluid Typography

remCSS uses clamp() for fluid text sizes — each token scales smoothly between two Golden Ratio steps as the viewport grows.

Base: 1rem = 10px

html { font-size: 62.5% } calibrates the rem base to 10px at the browser's default 16px. This is accessibility-safe — it's a relative value, so it still respects the user's browser font-size preference.

With this base, the Golden Ratio scale produces clean, intuitive pixel equivalents:

Each token fluidly scales between a minimum (narrow viewport) and a maximum (wide viewport), both derived from adjacent Golden Ratio steps.

TokenNarrow (min)Wide (max)Typical use
--text-xs6px (fixed)Labels, badges, captions
--text-sm10px16pxHelper text, footnotes
--text-base16px26pxBody paragraphs
--text-lg16px26pxLead paragraphs (scales faster)
--text-xl26px42pxh4 / sub-headings
--text-2xl26px42pxh3 (scales faster)
--text-3xl42px68pxh2 / section headings
--text-4xl42px68pxh1 (scales faster)
--text-5xl68px110pxHero / display headings

Why two tokens share the same min/max? --text-base and --text-lg both span the same GR step pair (16→26px), but --text-lg's viewport coefficient is larger — it reaches 26px at a narrower viewport. Same destination, different acceleration. Use --text-lg when you want the text to feel large earlier on tablet-sized screens.

The clamp() Pattern

Each token follows the pattern:

css
clamp(--step-N, Xrem + Yvi, --step-N+1)
  • min = GR step N (the natural minimum)
  • preferred = a viewport-relative expression that scales smoothly
  • max = GR step N+1 (exactly one φ-step up)
css
--text-base: clamp(var(--step-1), 1.3rem + 1vi, var(--step-2));
/*                 min: ~16px    preferred      max: ~26px     */

1vi is the inline viewport unit (equivalent to 1vw in horizontal writing modes), used instead of vw for writing-mode agnosticism.

Default Heading Sizes

Set automatically in the reset layer:

css
h1 {
	font-size: var(--text-4xl);
} /* 42 → 68px */
h2 {
	font-size: var(--text-3xl);
} /* 42 → 68px, fast */
h3 {
	font-size: var(--text-2xl);
} /* 26 → 42px */
h4 {
	font-size: var(--text-xl);
} /* 26 → 42px */
h5 {
	font-size: var(--text-lg);
} /* 16 → 26px */
h6 {
	font-size: var(--text-base);
} /* 16 → 26px */

Font Stacks

css
--font-sans:
	ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: ui-monospace, 'Cascadia Code', 'Fira Code', Consolas, 'Courier New', monospace;
--font-serif: ui-serif, Georgia, 'Times New Roman', serif;

The default font-family is --font-sans. Override per-element:

css
pre,
code {
	font-family: var(--font-mono);
}
blockquote {
	font-family: var(--font-serif);
}

Line Height Tokens

css
--line-height-tight: 1.15; /* headings */
--line-height-snug: 1.25; /* subheadings */
--line-height-normal: 1.618; /* body text — the Golden Ratio itself */
--line-height-relaxed: 2; /* extra airy body text */

--line-height-normal is φ itself — the proven ideal for comfortable body text reading.

Measure (Line Length)

css
--measure-narrow: 40ch;
--measure: 66ch; /* optimal: ~66 chars ≈ φ × 40.8 */
--measure-wide: 80ch;

The reset applies max-inline-size: var(--measure) to all <p> elements automatically. Override per context:

css
.lead {
	max-inline-size: var(--measure-wide);
	font-size: var(--text-lg);
}

Using Font Tokens Directly

html
<!-- Text size utilities don't exist — use tokens directly in components or inline -->
<p style="font-size: var(--text-sm); color: var(--color-on-surface-muted)">Last updated May 2026</p>

For repeated patterns, create a component class in your own stylesheet layered above remCSS, or use @layer utilities with a higher layer:

css
/* Your stylesheet, outside @layer, always wins over remCSS layers */
.caption {
	font-size: var(--text-xs);
	font-weight: var(--font-weight-medium);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
}