Perceivable · Level AA · 1.4.3
Contrast (Minimum)
Body text needs at least 4.5:1 contrast against its background. Large text — 18pt regular or 14pt bold and up, roughly 24px regular or 18.5px bold — gets a lower bar, 3:1, since bigger letterforms stay legible at lower contrast.
- Low vision
- Color blindness
How to recognize it
Pick out anything that reads as light gray on white, or a pastel color on a similarly light background — meta text, timestamps, placeholder-style labels, 'subtle' secondary copy — and run it through an actual contrast checker rather than eyeballing it. A very common real failure: a warning or accent-colored button (orange, light blue, mint) with white text on top, where the background itself is too light for white text to clear 4.5:1 even though it looks 'branded' and intentional. Another: text laid over a photo with a translucent overlay, where contrast depends on whatever happens to be behind it in that specific image, so it might pass over a dark patch of the photo and fail over a light one. Watch for the large-text exception being claimed incorrectly too — 16px bold text is not 'large text' under this criterion; the size threshold is specific and most body-sized bold text doesn't clear it.
How to fix it
Run every text/background color pair through a contrast checker and treat 4.5:1 as the real floor for normal text, reserving the 3:1 allowance for text that's actually large per the definition. Darken light grays rather than assuming a slightly muted color reads as 'accessible enough.' For colored buttons, check the specific text-on-background pair, not just whether the color matches the brand — a brand orange that works fine as an accent somewhere else can still fail once white text sits on top of it. For text over photos, use a solid or near-solid scrim (a dark overlay panel, not just a light gradient) behind the text so the contrast is fixed and verifiable once, instead of depending on whatever's in each individual photo.
Example
Avoid
p.meta {
color: #999999; /* ~2.85:1 on white */
}.btn-warning {
background: #f0ad4e;
color: #fff; /* ~1.95:1 — fails badly despite looking 'on-brand' */
}.hero-overlay-text {
color: #fff;
} /* sits directly on a photo with no scrim — contrast depends entirely on that image */Prefer
p.meta {
color: #595959; /* ~7:1 on white */
}.btn-warning {
background: #92400e;
color: #fff; /* ~7.1:1 */
}.hero-overlay {
background: rgba(0, 0, 0, 0.55); /* solid scrim, contrast no longer depends on the photo */
}
.hero-overlay-text {
color: #fff;
}