UI/UX Atlas
Typography Intermediate

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

Use WCAG 2.2 ratio checks as your compliance gate. Layer APCA on top to catch cases where technically passing text still looks poor — for example, a bold 36px heading at 3.1:1 that WCAG permits but which looks weak at actual screen resolution.

Don't

Treat APCA as a replacement for WCAG 2.2 AA. Some teams use APCA scores to justify reducing contrast on body text below 4.5:1, which is a legal compliance failure regardless of APCA scores.

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:

ContextMinimum (rem)Notes
Body copy1rem (16px)Baseline; never go smaller
UI labels, captions0.875rem (14px)Only acceptable with adequate contrast and weight
Legal / footnotes0.75rem (12px)Avoid in primary UI; must pass enhanced contrast if used
Large / display1.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: 40px and overflow: hidden that 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-describedby pointer 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 strong and em elements, 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

Run automated axe checks in CI to catch regressions early, then layer manual screen reader testing for complex reading-order and heading-hierarchy issues that automated tools miss.

Don't

Rely on automated tools alone. Lighthouse and axe catch roughly 30-40% of WCAG failures. Heading order, focus order, and meaningful link text require human judgment and screen reader verification.

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 rem or clamp(rem, ..., rem) — no bare px on 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