Typography Accessibility (WCAG Compliance)
Accessible typography goes far beyond contrast ratios — master the WCAG 2.2 rules, sizing, spacing, and APCA nuances that make text usable for everyone.
9 min read
The full lesson
Accessible typography is one of the highest-impact choices a designer can make. Poor contrast, tiny text, or rigid pixel sizes can make a product unusable for people with low vision, cognitive differences, or situational impairments. The fixes that help those users almost always improve the experience for everyone. WCAG 2.2 is the current legal and technical baseline — but understanding the reasoning behind each rule helps you go well beyond checkbox compliance.
Why Typography Is the Accessibility Frontline
Text carries most of the meaning in an interface. When type fails accessibility checks, users can’t read error messages, labels, instructions, or product names. Layout and color issues can sometimes be worked around. Unreadable text is a hard stop.
The scale of the problem is bigger than most teams expect. About 8% of men and 0.5% of women have some form of color vision deficiency. Around 246 million people worldwide have moderate to severe vision impairment. And situational factors — a cracked screen, bright sunlight, fatigue — create temporary low-vision conditions for everyone. Designing for permanent disability also improves the experience for situational and temporary impairment. This is called the “curb-cut effect.”
WCAG 2.2 Text Criteria You Must Know
WCAG 2.2 is the current stable version (as of 2026). Several criteria apply directly to typographic choices. Here are the most important ones.
1.4.3 Contrast (Minimum) — Level AA Normal text (below 18pt regular or 14pt bold) must achieve a contrast ratio of at least 4.5:1 against its background. Large text gets a relaxed threshold of 3:1. This is the legal baseline in most jurisdictions.
1.4.6 Contrast (Enhanced) — Level AAA Raises the bar to 7:1 for normal text and 4.5:1 for large text. Target AAA for critical reading contexts like financial dashboards, medical records, and long-form documentation.
1.4.4 Resize Text — Level AA
Text must be resizable up to 200% without losing content or functionality. Using px units for font sizes violates this criterion — browsers scale up the default font size during zoom, but px values don’t follow along.
1.4.12 Text Spacing — Level AA (added in WCAG 2.1) Content must remain readable when users override text spacing via their own stylesheets, using these values: line-height at least 1.5x the font size, letter spacing at least 0.12em, word spacing at least 0.16em, and paragraph spacing at least 2em. Your layouts must not break under these overrides.
1.4.8 Visual Presentation — Level AAA Covers maximum line length (no more than 80 characters), text justification (avoid full justification), line spacing (at least 1.5 in body), and resizable columns. Even at Level AA, following these guidelines dramatically improves readability.
Contrast Ratios in Practice
The WCAG contrast formula uses relative luminance — a measure of how much light a color emits. You divide the lighter color’s luminance by the darker one’s (adding 1 to each), and express the result as a ratio. White on black scores 21:1. White on a mid-gray is around 4.5:1.
Common Failures to Avoid
- Ghost text and placeholders. Placeholder text in form inputs often sits around 3:1 contrast. Use a real label instead of relying on placeholder text as a label — it fails on contrast and on criterion 1.3.5 (Identify Input Purpose).
- Low-contrast de-emphasis. Teams often reduce contrast to make secondary content look less important. This is not allowed: if text conveys information, it must pass contrast. Use size, weight, or spacing to create hierarchy instead of opacity or lightness.
- Text on images and gradients. Measure contrast at the worst point on the background. A gradient that goes from white to dark may pass at the dark end but fail at the light end.
- Thin font weights at body size. A weight-100 typeface at 14px may technically pass contrast on a white background but still feel unreadable because the strokes within the glyphs are so thin.
The APCA Lens
APCA (Advanced Perceptual Contrast Algorithm) is a proposed replacement for the WCAG 2.x contrast formula. It accounts for text size and weight — called spatial frequency — rather than treating all text the same. Instead of ratios, APCA produces Lightness Contrast (Lc) values.
APCA is more perceptually accurate than the current WCAG formula, which was calibrated for CRT displays. However, WCAG 3.0 (which would include APCA) is still a working draft and has not been adopted. Use APCA as a supplementary quality check — especially for large display type and UI labels where the WCAG 2.2 formula can be overly conservative — but always verify AA compliance with the WCAG 2.2 ratio algorithm.
Do
Don't
Font Sizing and the Zoom Requirement
WCAG 1.4.4 requires that text scales to 200% without losing content or functionality. In practice: never use px units for font-size in body or UI text.
Modern approach: use rem for base sizes combined with clamp() for fluid scaling:
/* Fluid body text: scales between 1rem (16px) and 1.125rem (18px) */
body {
font-size: clamp(1rem, 0.875rem + 0.25vw, 1.125rem);
}
The rem unit respects the user’s browser default font size and zoom preference. px overrides it. This is not a pedantic distinction — many users and people with low vision set their browser default above 16px, and px values silently ignore that.
Minimum sizes for different contexts:
| Context | Minimum (rem) | Notes |
|---|---|---|
| Body copy | 1rem (16px) | Baseline; never go smaller |
| UI labels, captions | 0.875rem (14px) | Only acceptable with adequate contrast and weight |
| Legal / footnotes | 0.75rem (12px) | Avoid in primary UI; must pass enhanced contrast if used |
| Large / display | 1.5rem+ | Qualifies for relaxed 3:1 contrast threshold |
Text Spacing Overrides (1.4.12)
WCAG 1.4.12 does not require you to set these spacing values by default. It requires that your layout does not break when users apply them. Test by injecting a bookmarklet or user stylesheet with:
* {
line-height: 1.5 !important;
letter-spacing: 0.12em !important;
word-spacing: 0.16em !important;
}
p {
margin-bottom: 2em !important;
}
Common failures:
- Fixed-height containers that clip text when line-height increases
- Buttons with
height: 40pxandoverflow: hiddenthat cut off taller glyphs - Truncated text inside cards that breaks layout when letter-spacing increases width
Fix pattern: use min-height instead of height. Always test critical UI with the spacing bookmarklet before shipping.
Semantic Structure and Reading Order
Accessible typography is not only about visual properties — it also includes how text is encoded in the DOM.
Heading hierarchy. Screen readers let users navigate by heading level. Skipping from an h2 to an h4 creates a gap that breaks this navigation. Your visual type scale should map cleanly to semantic heading levels: the main title gets h1, section headers get h2, subsections get h3, and so on. Do not choose heading levels for their default visual size — override size with CSS.
Lists and structured content. Bullet lists built with dashes or dot characters inside plain p tags deny screen reader users the ability to know the list count or jump between items. Use ul, ol, and li elements.
Decorative vs. informative text. Purely decorative text — a background watermark, a pull-quote that duplicates nearby body copy — can be hidden from assistive technology with aria-hidden="true". Text that carries information, even if styled as decoration, must stay accessible.
Line Length, Justification, and Dyslexia
WCAG 1.4.8 (Level AAA) caps line length at 80 characters for column text. This is a cognitive load constraint, not a style preference. Long lines force readers to track back from the end of one line to the start of the next, and they lose their place more easily.
Full justification (text aligned to both edges) creates uneven “rivers” of whitespace. These are especially disorienting for users with dyslexia or reading disorders. Left-align body text in almost all cases. Centered text is fine for short headings but should never be used for multi-line body copy.
Relevant CSS properties:
/* Enforce a readable measure */
.prose {
max-width: 65ch;
text-align: left;
/* Prevent uneven hyphenation on narrow screens */
hyphens: auto;
overflow-wrap: break-word;
}
The ch unit is ideal here because it scales with the font’s “0” glyph width, which tracks actual character width reasonably well.
Color and Typography Together
Color should never be the only carrier of typographic meaning. This applies to:
- Error states: A red input label is not enough. Pair color with an error icon, a text prefix like “Error:”, and an
aria-describedbypointer to the message. - Required fields: An asterisk in red may technically pass criterion 1.4.1 (Use of Color) if the convention is explained, but the cleaner pattern is visible “(required)” text.
- Emphasis: Bold and italic are semantically meaningful alternatives to color-only emphasis. Use
strongandemelements, not just visual styling.
When defining color tokens for text, use OKLCH rather than HSL or hex. OKLCH is perceptually uniform — a step change in L (lightness) produces a predictable change in perceived contrast across hues. This matters when generating accessible semantic text tokens programmatically. In HSL, two colors with identical L values on yellow and blue can look completely different in brightness.
/* OKLCH semantic text tokens */
:root {
--color-text-primary: oklch(20% 0.01 260); /* ~16:1 on white */
--color-text-secondary: oklch(42% 0.02 260); /* ~7:1 on white */
--color-text-tertiary: oklch(55% 0.02 260); /* ~4.6:1 on white */
--color-text-disabled: oklch(65% 0.01 260); /* intentionally sub-AA */
}
Disabled text is explicitly exempt from WCAG contrast requirements — but this exemption is narrow and often abused. Apply it only to truly non-interactive, non-informative states.
Testing and Tooling
No mental model replaces actual testing. Build these checks into your workflow.
Automated: Axe DevTools, IBM Equal Access Checker, and Lighthouse all catch contrast failures, missing headings, and non-semantic list markup. Automate in CI with axe-core against a component test suite.
Manual checks:
- Zoom the browser to 200% and confirm no content is lost
- Inject the 1.4.12 text spacing stylesheet
- Navigate the page with a screen reader (VoiceOver on macOS, NVDA on Windows) and listen to heading flow
- Disable CSS and verify the reading order makes sense in the unstyled document
Figma: The Contrast plugin and built-in accessibility annotations catch contrast issues at the design stage — far cheaper than fixing in code.
Colour Contrast Analyser (Paciello Group, free): eyedropper any color on screen and get instant WCAG and APCA scores. Invaluable for text on images or complex backgrounds.
Do
Don't
Practical Checklist Before Shipping
Run through this before any component or page ships:
- All body text passes 4.5:1 contrast ratio (checked with a tool, not eyeballed)
- Large text (18pt+ regular, 14pt+ bold) passes 3:1
- Font sizes use
remorclamp(rem, ..., rem)— no barepxon text - No
height-clipping containers that would fail 1.4.12 at increased spacing - Heading levels are sequential with no skipped levels
- Error, warning, and status text uses more than color alone
- Placeholder text is not the sole label for form fields
- Line length is capped around 65-80ch for multi-line body copy
- Page zoomed to 200% in browser — no content clips or overlaps