The part a checklist will not tell you
An automated accessibility audit will confirm your contrast ratios and your heading order. It will pass a page that is genuinely exhausting to read, because the properties that make prose readable for a dyslexic reader (glyph disambiguation, character orientation, line rhythm, the absence of reflow) are not in the checklist.
The gap is not an oversight in the tooling. Contrast is measurable from a pair of colours. Whether b, d, p and q are rotationally confusable in a given typeface is a property of the letterforms, and no linter has an opinion about it.
Three jobs, three typefaces
The instinct is to pick one accessible font and apply it everywhere. That trades one compromise for another, because headings and body prose are doing different work.
- Headings: Lexend. Designed around reading-speed research, with expanded spacing and distinct silhouettes. Headings are scanned, not read; silhouette matters more than fine detail.
- Prose and UI: Atkinson Hyperlegible. Commissioned by the Braille Institute specifically to disambiguate the pairs that collapse in most grotesques: uppercase I, lowercase l, and the numeral 1. Body text is read linearly, so per-character certainty is what counts.
- Opt-in mode: OpenDyslexic. Weighted bottoms anchor each glyph's orientation, which is what prevents the rotational flipping that makes letters "swim".
OpenDyslexic is deliberately an option, not a default. The evidence for dyslexia-specific typefaces is genuinely mixed, and imposing one on every reader substitutes a different assumption for the one you removed. Making it a one-click persistent toggle respects that the reader knows their own needs better than the research aggregate does.
Layout shift is an accessibility bug
This is the part usually filed under performance, and it does not belong there.
A reader who has lost their place in a paragraph because the page reflowed has to re-find it. For a fluent reader that costs a fraction of a second. For someone who is decoding effortfully, it can mean restarting the paragraph. Cumulative Layout Shift is a cognitive cost before it is a performance metric, and a font switch is the most disruptive shift there is, because it moves every line simultaneously.
Which means a typography toggle has an unusually strict requirement: it must produce zero layout shift. Not "small". Zero. That rules out swapping a stylesheet and hoping metrics are close, and it rules out reading the preference in an effect after hydration; by then the first paint has already happened in the wrong font.
// Measure before paint, not after mount.
// Reading persisted preference in an effect guarantees a flash: the
// server-rendered markup has already committed to the default face.
const mode = useSyncExternalStore(
subscribeToTypographyMode,
getTypographyMode, // client snapshot
getServerTypographyMode // server snapshot - must not read storage
);
The general technique: precompute text layout against known font metrics rather than letting the browser discover them, so the space a block will occupy is decided before anything renders into it. Swapping the face then changes glyphs without moving lines.
Why this is worth the effort
Accessible typography is usually justified as compliance, which is true and also the least interesting reason. The honest version is narrower: someone specific could not read the thing you made, and you could fix it.
The techniques generalise to a reader with a migraine, a reader on a phone in sunlight, or a reader in their fourth hour of a long shift. But they generalise because they were designed for a hard case rather than an average one. Designing for the average produces the geometric grotesque everyone already ships.
The full build (the tripartite hierarchy, the zero-FOUT initialisation, and the Pretext reflow engine) is documented in Designing for My Brother.