Accessible and Responsive Type
Accessible typography is not a constraint on good design. It is the measure of whether the design works for the people who need it most.
Accessible typography is typography that works for the full range of people who will encounter it — including those with low vision, dyslexia, cognitive differences, or simply older eyes in poor lighting. The WCAG guidelines establish minimum standards: contrast ratios, minimum sizes, reflow requirements. But accessibility is not a checklist. It is a design quality, present or absent in every typographic decision made throughout a system.
Contrast ratios
WCAG 2.1 requires a contrast ratio of at least 4.5:1 for normal text (under 18pt / 24px) and 3:1 for large text (18pt / 24px and above, or 14pt / ~19px bold). These are minimum thresholds. The comfortable midpoint for body text on a white background is around 7:1 — the contrast ratio of black text (#000000) — which is also the WCAG AAA standard.
In practice, many interfaces use body text in the range of #444444 to #555555 on white, which gives ratios between 9.7:1 and 7.5:1 — well above the 4.5:1 minimum and comfortable for most readers. The problem emerges with secondary text: placeholder text, helper text, and captions are often designed at low contrast to signal their secondary status. A common choice like #999999 on white gives 2.85:1, which fails WCAG AA for normal-sized text entirely. Hierarchy can be communicated through size and position; it doesn’t need to be communicated by making text hard to read.
Minimum sizes
WCAG does not specify a minimum font size, but the consensus from readability research and platform guidelines is 16px for body text on screen. Below this, reading speed decreases and error rates increase, particularly for users with reduced acuity. For UI text (labels, captions), 12px is typically the practical minimum, and only with appropriate contrast and weight.
On touch interfaces, text that also serves as a tap target must meet touch target minimums — typically 44×44 points — independent of type size. Small text on a large touch target is functional. Small text on a small touch target is both unreadable and difficult to interact with.
Fluid scaling and user preferences
Responsive typography is not just about adjusting type across breakpoints. It is also about respecting user preferences. Users can set a larger default font size in their browser or OS. A typographic system built on rem units respects this setting: all type scales proportionally when the user’s default size increases. A system built entirely on px ignores the user’s setting entirely.
The accessible approach is to use rem for font sizes and set the base size on the root element as a multiplier of the browser default (e.g. html { font-size: 100%; } rather than html { font-size: 16px; }). This allows fluid scaling to compound with the user’s preferences rather than overriding them.
Web font loading
Web fonts introduce a performance dependency: the browser cannot display text in a custom font until the font file has downloaded. This creates two failure modes — FOIT (Flash of Invisible Text, where text is hidden until the font loads) and FOUT (Flash of Unstyled Text, where text renders in a fallback font then reflows). The font-display descriptor controls this:
@font-face {
font-family: 'Inter';
src: url('Inter-Variable.woff2') format('woff2');
font-display: swap;
}
font-display: swap renders text in a fallback font immediately and swaps when the custom font loads — visible text always, at the cost of a reflow. font-display: optional only renders the custom font if it loads within the first 100ms; on repeat visits from cache this works reliably, on first load it falls back gracefully. For body text, swap is the right choice. For non-critical display type, optional avoids layout shift at the cost of occasionally showing the system font.
Preload critical fonts in the document <head> to reduce the initial delay:
<link rel="preload" href="/fonts/Inter-Variable.woff2" as="font" type="font/woff2" crossorigin>
Choose a fallback font with similar metrics (x-height, ascender/descender) to your custom font to minimise reflow when the swap occurs. The CSS size-adjust, ascent-override, and descent-override descriptors allow precise metric matching to reduce visual shift.
Fluid typography with clamp()
The CSS clamp() function allows type to scale smoothly between a minimum and maximum size relative to the viewport, without abrupt changes at breakpoints:
font-size: clamp(1rem, 0.75rem + 1.5vw, 1.5rem);
The three values are: minimum size (never smaller than 1rem), preferred size (grows with viewport width), and maximum size (never larger than 1.5rem). This keeps body text legible at small viewports and proportionate at large ones without a single breakpoint.
When using clamp(), the minimum value should remain in rem so it respects the user’s browser font-size preference. The viewport-relative middle value handles the fluid range. The maximum caps growth at a comfortable reading size. See Measure and the Reading Line for how measure should scale alongside font size.