Typography
Twelve roles, not twelve sizes. Each token carries its own line-height and tracking, so one utility reproduces a style exactly and no component ever needs to set three properties to get one look.
abcdefghijklmnopqrstuvwxyz
0123456789 4.7 · 3.2 · 0.0 –– …
body, captionseyebrows, linksheadings, buttonsrare — figures onlyThe scale below is tuned to DM Sans metrics — twelve sizes, each with its own line-height and tracking. Substituting the typeface means re-tuning all twelve, which is why it is not a swap anyone makes casually. --font-legacy (Inter) exists so legacy markup resolves; new work must not use it.
The scale
h2, h1 and display are clamp() — they resize with the viewport, no breakpoint overrides. display is hero-only and only in comfortable density.
Every density shares the scale
A dashboard is not a different typeface at a different scale. It is the same voice speaking in shorter sentences — the change is in the copy and the rhythm, never the type ramp.
Every athlete who rates an event gets a written debrief the same week.
Debrief sent to 412 athletes.
Uppercase is a style, never a string
Eyebrows, badges and table heads are uppercase, applied by text-transform in three recipes. A screen reader reads SUBMIT as an initialism; text-transform does not have that problem.
<Eyebrow>How it works</Eyebrow><Eyebrow>HOW IT WORKS</Eyebrow>Numbers
Any figure in a column, a bar or a table gets tabular-nums. Ratings are always one decimal — 4.0, not 4. A trailing .0 signals a measured average rather than a whole-number opinion.
3.1
11.0
0.9
3.1
11.0
0.9
Measures
Text never spans the viewport. Pick the measure by what the column contains, not by what fits.
--container-form · 28rem--container-prose · 36rem--container-narrow · 42remThe editorial serifs
Cut. --font-editorial-display (Playfair Display) and --font-editorial-body (Source Serif 4) were declared and never loaded, so they shipped as Georgia while reading as a decision in review. Neither token exists any more.
DM Sans is the whole typographic system. If /business/ later needs an editorial voice, add one family with its @font-face in the same commit that first uses it — two unloaded families on an SEO-first surface is a cost with no return.
--font-editorial-display (Playfair Display) and --font-editorial-body (Source Serif 4) are declared but never loaded — no @import fires for either. Right now they are two names that silently fall back to Georgia.
Recommendation: cut them. A token that resolves to a fallback is worse than no token — it looks intentional in review and ships as Georgia. If /business/ later needs an editorial voice, add one family with its @import in the same commit. Two unloaded families on an SEO-first surface is a cost with no return.