Assemble a complete, production-ready typography design system: loading strategy, fluid scale tokens, OpenType features, dark mode, and documentation — ready to drop into any project.
A typography design system is not a stylesheet — it is a set of documented decisions. Every token should have a reason. Every loading choice should have a justification. Every scale step should map to a semantic role. This capstone lesson assembles every concept from the module into a single, coherent, documented system that is genuinely ready for production use.
A typography design system has four layers. Layer 1 — Font loading: @font-face declarations, preload hints, font-display strategy, unicode-range subsets. Layer 2 — Scale tokens: CSS custom properties for every font size step, using fluid clamp() values. Layer 3 — Role tokens: semantic properties (--font-body, --font-display, --weight-heading) that reference scale tokens. Layer 4 — Application: selectors that apply role tokens to semantic HTML elements (body, h1–h6, code, blockquote, caption). The principle: changes to Layer 1 (swapping a font file) should never require changes to Layer 3 or 4. Changes to Layer 2 (adjusting the scale ratio) should automatically propagate to Layer 4. This separation is what makes the system maintainable.
1/* Layer 1: Font loading */
2@font-face { font-family: 'Inter'; /* ... */ }
3@font-face { font-family: 'Fraunces'; /* ... */ }
4
5/* Layer 2: Scale tokens */
6:root {
7 --text-xs: clamp(0.694rem, 0.663rem + 0.16vw, 0.80rem);
8 --text-sm: clamp(0.833rem, 0.796rem + 0.19vw, 0.958rem);
9 --text-base: clamp(1rem, 0.956rem + 0.22vw, 1.125rem);
10 --text-lg: clamp(1.333rem, 1.273rem + 0.30vw, 1.5rem);
11 --text-xl: clamp(1.777rem, 1.697rem + 0.40vw, 2rem);
12 --text-2xl: clamp(2.369rem, 2.263rem + 0.53vw, 2.667rem);
13 --text-3xl: clamp(3.157rem, 3.015rem + 0.71vw, 3.556rem);
14}
15
16/* Layer 3: Role tokens */
17:root {
18 --font-body: 'Inter', 'InterFallback', system-ui, sans-serif;
19 --font-display: 'Fraunces', Georgia, serif;
20 --font-code: 'JetBrains Mono', Consolas, monospace;
21 --wt-body: 400; --wt-heading: 700; --wt-bold: 700;
22 --lh-body: 1.65; --lh-heading: 1.15; --lh-display: 1.0;
23}
24
25/* Layer 4: Application */
26body { font-family: var(--font-body); font-size: var(--text-base); }
27h1 { font-family: var(--font-display); font-size: var(--text-3xl); }The full set of typography tokens for a production system covers: scale, font families, weights, line heights, letter spacing, and colour. Every token is named at the semantic level (--text-heading) rather than the value level (--text-32px) so that values can change without renaming tokens.
1/* typography-tokens.css — complete reference */
2:root {
3 /* === SCALE (Perfect Fourth, fluid 320px → 1440px) === */
4 --text-xs: clamp(0.694rem, 0.663rem + 0.16vw, 0.80rem);
5 --text-sm: clamp(0.833rem, 0.796rem + 0.19vw, 0.958rem);
6 --text-base: clamp(1rem, 0.956rem + 0.22vw, 1.125rem);
7 --text-lg: clamp(1.333rem, 1.273rem + 0.30vw, 1.5rem);
8 --text-xl: clamp(1.777rem, 1.697rem + 0.40vw, 2rem);
9 --text-2xl: clamp(2.369rem, 2.263rem + 0.53vw, 2.667rem);
10 --text-3xl: clamp(3.157rem, 3.015rem + 0.71vw, 3.556rem);
11
12 /* === FAMILIES === */
13 --font-body: 'Inter', 'InterFallback', system-ui, sans-serif;
14 --font-display: 'Fraunces', Georgia, serif;
15 --font-code: 'JetBrains Mono', Consolas, 'Courier New', monospace;
16
17 /* === WEIGHTS === */
18 --wt-light: 300;
19 --wt-regular: 400;
20 --wt-medium: 500;
21 --wt-semibold:600;
22 --wt-bold: 700;
23 --wt-extrabold:800;
24
25 /* === LINE HEIGHTS === */
26 --lh-tight: 1.1;
27 --lh-snug: 1.25;
28 --lh-normal: 1.5;
29 --lh-relaxed: 1.65;
30 --lh-loose: 1.8;
31
32 /* === LETTER SPACING === */
33 --ls-tight: -0.025em;
34 --ls-snug: -0.015em;
35 --ls-normal: 0;
36 --ls-wide: 0.05em;
37 --ls-wider: 0.08em;
38 --ls-widest: 0.12em;
39
40 /* === MEASURE === */
41 --measure-narrow: 45ch;
42 --measure-body: 65ch;
43 --measure-wide: 80ch;
44}The token system extends cleanly to dark mode: only colour tokens and weight tokens change. Scale tokens, family tokens, and measure tokens remain the same. This is the correct architecture — swapping colour scheme requires only colour overrides, never layout changes.
1/* Dark mode: only colours and weights change */
2@media (prefers-color-scheme: dark) {
3 :root {
4 /* Colours — recalculated for #0f172a background */
5 --color-text: #f1f5f9; /* 14.6:1 — AAA */
6 --color-text-muted: #94a3b8; /* 5.3:1 — AA */
7 --color-bg: #0f172a;
8 --color-bg-raised: #1e293b;
9
10 /* Weight compensation for dark-bg halation */
11 --wt-regular: 350;
12 --wt-medium: 450;
13 --wt-semibold: 550;
14 --wt-bold: 650;
15
16 /* Slightly looser leading in dark mode */
17 --lh-relaxed: 1.70;
18
19 /* Antialiasing */
20 --font-smoothing: antialiased;
21 --mac-font-smoothing: grayscale;
22 }
23}
24
25body {
26 background-color: var(--color-bg, #ffffff);
27 color: var(--color-text, #1e293b);
28 -webkit-font-smoothing: var(--font-smoothing, auto);
29 -moz-osx-font-smoothing: var(--mac-font-smoothing, auto);
30}Before shipping any typography system, verify every WCAG criterion that touches text. This checklist covers 1.4.3 (contrast), 1.4.4 (resize), 1.4.12 (text spacing), and 3.1.1 (language).
1WCAG 2.1 AA Typography Checklist:
2
31.4.3 Contrast (Minimum):
4 ☐ Body text (#1e293b on #fff): 15.3:1 ✓ (needs 4.5:1)
5 ☐ Secondary text (#767676 on #fff): 4.54:1 ✓ (barely passes)
6 ☐ Dark mode body (#f1f5f9 on #0f172a): 14.6:1 ✓
7 ☐ Dark mode secondary (#94a3b8 on #0f172a): 5.3:1 ✓
8 ☐ Focus indicator: 3:1 against adjacent colour ✓
9
101.4.4 Resize Text:
11 ☐ html { font-size: 100% } — not px ✓
12 ☐ All font sizes in rem ✓
13 ☐ No content lost at 200% browser zoom ✓
14
151.4.12 Text Spacing:
16 ☐ No fixed-height containers on text ✓
17 ☐ No overflow:hidden on text containers ✓
18 ☐ Padding/margin in em not px ✓
19 ☐ Tested with WCAG bookmarklet ✓
20
213.1.1 Language of Page:
22 ☐ <html lang="en"> set ✓1/* Practical: test spacing override */
2/* Paste in DevTools console to run 1.4.12 test */
3const s = document.createElement('style');
4s.textContent = '* { line-height: 1.5 !important;
5 letter-spacing: 0.12em !important;
6 word-spacing: 0.16em !important; }
7 p { margin-bottom: 2em !important; }';
8document.head.append(s);
9// Visually inspect — remove: s.remove()A design token system is most valuable when it is a single source of truth shared between CSS and design tools (Figma, Storybook, Style Dictionary). The W3C Design Tokens format (JSON) provides a standard interchange format. Style Dictionary (Amazon) transforms a tokens.json into CSS custom properties, Sass variables, JavaScript constants, and Figma token files from one source. For smaller projects, a simple tokens.json that maps directly to CSS custom property names is sufficient.
1{
2 "typography": {
3 "scale": {
4 "xs": { "value": "clamp(0.694rem, 0.663rem + 0.16vw, 0.80rem)", "type": "fontSizes" },
5 "sm": { "value": "clamp(0.833rem, 0.796rem + 0.19vw, 0.958rem)", "type": "fontSizes" },
6 "base": { "value": "clamp(1rem, 0.956rem + 0.22vw, 1.125rem)", "type": "fontSizes" },
7 "lg": { "value": "clamp(1.333rem, 1.273rem + 0.30vw, 1.5rem)", "type": "fontSizes" },
8 "xl": { "value": "clamp(1.777rem, 1.697rem + 0.40vw, 2rem)", "type": "fontSizes" },
9 "2xl": { "value": "clamp(2.369rem, 2.263rem + 0.53vw, 2.667rem)", "type": "fontSizes" },
10 "3xl": { "value": "clamp(3.157rem, 3.015rem + 0.71vw, 3.556rem)", "type": "fontSizes" }
11 },
12 "families": {
13 "body": { "value": "Inter, InterFallback, system-ui, sans-serif", "type": "fontFamilies" },
14 "display": { "value": "Fraunces, Georgia, serif", "type": "fontFamilies" },
15 "code": { "value": "JetBrains Mono, Consolas, monospace", "type": "fontFamilies" }
16 }
17 }
18}The full production typography system: loading, tokens, application, and dark mode in one file.
You have completed the Web Typography module. Move on to Design Collaboration to learn how to receive design specs, extract CSS from Figma Dev Mode, and implement designs with pixel-perfect precision.
Your progress is saved in this browser, on this device only.