Build typography that meets WCAG 1.4.12 Text Spacing, passes contrast requirements for all text sizes, and accommodates dyslexic readers — without breaking your design.
Accessible typography is not about aesthetics — it is about whether people can actually read your content. WCAG 2.1 has specific, testable criteria for text: contrast ratios, spacing overrides, and zoom behaviour. A typography system that passes all of these requires intentional engineering from the start, not bolt-on fixes. This lesson covers every WCAG criterion that directly touches typography, with specific CSS implementation for each.
WCAG 2.1 Success Criterion 1.4.12 (AA) requires that no loss of content or functionality occurs when users override text spacing to: line height at least 1.5× font size, spacing after paragraphs at least 2× font size, letter spacing at least 0.12× font size, word spacing at least 0.16× font size. This test catches layouts where text is clipped in fixed-height containers, buttons that break when text wraps, and tooltips that overflow their containers. The fix: avoid fixed heights on text containers, use min-height instead; avoid overflow: hidden on elements containing text; use em or rem for all spacing rather than px values that do not scale. Test by applying the WCAG override bookmarklet or this CSS snippet directly in DevTools.
1/* WCAG 1.4.12 override test — paste in DevTools to test any site */
2* {
3 line-height: 1.5 !important;
4 letter-spacing: 0.12em !important;
5 word-spacing: 0.16em !important;
6}
7p { margin-bottom: 2em !important; }
8
9/* Your layout CSS should survive this without clipping or overflow */
10
11/* Safe pattern: min-height instead of height for text containers */
12.button {
13 min-height: 2.75rem; /* Allows text to expand vertically */
14 padding-block: 0.5rem;
15 padding-inline: 1.25rem;
16 /* No overflow: hidden — text must be able to wrap */
17}
18
19.tooltip {
20 max-width: 20ch;
21 /* Allow natural height growth — no fixed height */
22}WCAG 1.4.3 (AA) requires: normal text (under 18pt/24px regular or 14pt/18.67px bold) needs 4.5:1 contrast ratio. Large text (18pt/24px+ regular or 14pt/18.67px+ bold) needs 3:1. WCAG 1.4.11 (AA) requires UI components and graphical objects (borders of inputs, focus indicators, chart lines) to have a 3:1 contrast ratio against adjacent colours. For dark mode, recalculate every contrast pair — a text colour that passes on a white background may fail on a dark background. The APCA (Advanced Perceptual Contrast Algorithm) proposed for WCAG 3.0 is more nuanced — it accounts for font weight and size together. For production work, use the APCA contrast checker alongside WCAG ratios.
1:root {
2 /* Light mode — verified ratios against #ffffff */
3 --text-primary: #1a1a1a; /* 18.1:1 — exceeds AAA */
4 --text-body: #333333; /* 12.6:1 — exceeds AAA */
5 --text-secondary: #767676; /* 4.54:1 — passes AA normal text */
6 --text-caption: #595959; /* 7.0:1 — passes AAA normal text */
7
8 /* Input border: must be 3:1 against white background */
9 --border-input: #767676; /* 4.54:1 — passes 1.4.11 */
10}
11
12/* Focus indicator: 3:1 against both adjacent colours */
13:focus-visible {
14 outline: 2px solid #005fcc; /* 7.2:1 on white */
15 outline-offset: 2px;
16 /* Background behind outline: check ratio of #005fcc against page bg */
17}Dyslexia affects 15–20% of the population and makes certain typographic choices significantly more difficult to read. Key accommodations: avoid fonts with high letterform similarity between b/d and p/q (common in geometric typefaces like Futura — the b is literally a rotated d). Use fonts with clear differentiation between these pairs: Inter, Lexend, and OpenDyslexic all score well. Avoid justified text — the variable word spacing it creates makes word identification harder. Use left-aligned (ragged right) text instead. Increase line height to 1.5–1.6 minimum — more vertical space between lines reduces visual crowding. Use a slightly larger font size for body text (17–18px rather than 16px). Keep paragraphs short (3–4 sentences) and use white space generously. These accommodations improve readability for all users, not just those with dyslexia.
1/* Dyslexia-friendly body text */
2.prose {
3 /* Clear, open typeface with distinct letterforms */
4 font-family: 'Lexend', 'Inter', system-ui, sans-serif;
5
6 /* Slightly larger base size */
7 font-size: clamp(1.0625rem, 0.97rem + 0.47vw, 1.25rem);
8
9 /* Generous leading — reduces visual crowding */
10 line-height: 1.7;
11
12 /* Left-aligned: consistent word spacing */
13 text-align: left;
14
15 /* Constrained measure: 65–70ch optimal */
16 max-width: 66ch;
17
18 /* Generous paragraph spacing */
19 p { margin-block-end: 1.8em; }
20}
21
22/* Avoid justified text for body content */
23.prose p {
24 hyphens: none; /* No hyphenation — another dyslexia pain point */
25}WCAG 1.4.4 requires content to be resizable up to 200% without loss of content or functionality. If you set font-size in px at the root level (html { font-size: 16px }), you override the user's browser font size preference. Users who have set their browser to 20px base size see 16px — your design has stolen their accessibility setting. Instead, use html { font-size: 100% } (inherits browser default) and then rem units throughout. 1rem = whatever the user's browser font size is (typically 16px, but may be 18px, 20px, or 24px for users with low vision). All your clamp() values should use rem, not px. One exception: border widths and decorative spacing can use px safely — they do not need to scale with text.
1/* Correct: respects user browser font preferences */
2html {
3 font-size: 100%; /* Inherit browser default (usually 16px) */
4}
5
6body {
7 /* rem units scale with user's chosen browser font size */
8 font-size: clamp(1rem, 0.92rem + 0.4vw, 1.25rem);
9 line-height: 1.6;
10}
11
12h1 { font-size: clamp(2rem, 1.5rem + 2.5vw, 4rem); }
13
14/* Wrong: locks out user font size preferences */
15html { font-size: 16px; } /* Overrides user setting */
16body { font-size: 16px; } /* Same problem */
17
18/* Spacing: em/rem for text-adjacent, px ok for decorative */
19.card {
20 padding: 1.5rem; /* Scales with text */
21 border-radius: 8px; /* Decorative — px is fine */
22 border: 1px solid #e5e7eb; /* Decorative — px is fine */
23 margin-block-end: 1.5rem; /* Spacing around text — rem */
24}Three levels of typography accessibility audit: automated, manual, and user testing. Automated: run axe DevTools or Lighthouse accessibility audit — these catch missing contrast ratios, missing lang attributes on html element, and missing text alternatives. They cannot catch WCAG 1.4.12 Text Spacing failures (requires manual test). Manual: apply the Text Spacing bookmarklet and scan every page at 200% zoom. Check that all text containers expand naturally. Check that button text does not overflow. Check that form labels remain associated with their inputs. Check that tooltips and popovers do not clip content. User testing: involve users with dyslexia, low vision, and COGA (cognitive and learning disabilities) at least once per design system update cycle.
1// Paste in browser console to check all text contrast ratios
2// (simplified — use axe or Lighthouse for production auditing)
3const texts = document.querySelectorAll('p, h1, h2, h3, h4, span, a');
4texts.forEach(el => {
5 const style = getComputedStyle(el);
6 const color = style.color;
7 const bg = style.backgroundColor;
8 // Use a contrast library (e.g., @accessibility-checker/core)
9 // to calculate WCAG contrast ratio from color/bg pair
10 console.log(el.tagName, color, 'on', bg);
11});
12
13/* Quick WCAG 1.4.12 test in DevTools Console: */
14document.head.insertAdjacentHTML('beforeend', `
15 <style id="wcag-test">
16 * { line-height: 1.5 !important;
17 letter-spacing: 0.12em !important;
18 word-spacing: 0.16em !important; }
19 p { margin-bottom: 2em !important; }
20 </style>
21`);
22// Scan page visually — remove when done:
23document.getElementById('wcag-test').remove();A complete accessible typography setup that passes 1.4.3, 1.4.4, 1.4.11, and 1.4.12 out of the box.
Next lesson covers pairing typefaces — the principles and CSS implementation for combining a display face with a body face, including contrast, classification, and weight pairings.
Your progress is saved in this browser, on this device only.