( Perceivable / WCAG 1.4.3 )
Text does not have enough colour contrast
What is this issue?
The contrast ratio between a block of text and its background is below WCAG’s minimum: 4.5:1 for normal text, 3:1 for text at 18.66px bold or 24px regular and larger. Contrast ratio is a number from 1:1 (identical colors, completely unreadable) to 21:1 (pure black on pure white, maximum possible contrast), calculated from the relative brightness (the luminance) of the two colors, not just how different they look.
A ratio of 4.5:1 means the lighter of the two colors reflects roughly four and a half times more light than the darker one, by that luminance formula. Light grey text on a white background (a common design choice for “softer” body copy) frequently lands around 2–3:1, well under the minimum.
Why does this matter?
People with low vision, color blindness, or age-related vision changes need a real difference in brightness between text and background to distinguish letterforms at all, not just a color difference a person with typical vision can perceive. Someone with red-green color blindness, for instance, sees a red heading on a green background as two very similar shades of grey; without brightness contrast to fall back on, the text effectively disappears.
Low contrast also degrades reading for people with no diagnosed vision condition: anyone using a phone outdoors in sunlight, an older monitor with washed-out colors, or a screen at low brightness to save battery. A contrast failure isn’t a rare edge case; it’s one of the most common reasons a page that looks fine in a dim office is unreadable everywhere else.
Who is affected?
- Low vision users: need a real brightness difference between text and background to distinguish letterforms; color difference alone isn’t enough for many low-vision and color-blind users, who perceive insufficiently contrasting colors as nearly identical shades.
What users experience
Elena has moderate low vision and reads most websites at 150% browser zoom with the brightness turned down to reduce eye strain. On a checkout page, the “Free shipping over $50” banner uses light blue text on a slightly darker blue background, a combination that measures 2.1:1. At her normal screen settings, the banner is a blur of color with no legible message, so she scrolls past it without realizing she’s $12 short of free shipping until after she pays for delivery.
How do I fix it?
Adjust the text color, the background color, or both, until a contrast checker confirms the ratio meets the minimum for that text’s size. This works because contrast ratio is purely a function of the two colors’ luminance values; there’s no other variable to fix, which makes this one of the most mechanically verifiable rules in this whole rule set.
Two exceptions change the target ratio: text at 18.66px bold (or 24px regular) and larger only needs 3:1, since larger glyphs stay legible at lower contrast; and text that’s part of a logo or purely decorative with no informational role is exempt from the requirement entirely. Everything else (body copy, labels, placeholder text, disclaimers, footer links) needs the full 4.5:1.
Use a contrast-checking tool rather than eyeballing it: browser DevTools’ color picker shows a live ratio as you adjust either color, and standalone checkers like WebAIM’s let you test a pair of hex values before you touch any code.
Code Examples
/* Ratio: ~2.3:1 - fails the 4.5:1 minimum */
.disclaimer-text {
color: #999999;
background: #ffffff;
}/* Ratio: ~7:1 - passes both AA and AAA */
.disclaimer-text {
color: #595959;
background: #ffffff;
}Both versions use the same light, muted grey concept designers reach for on secondary text; only the specific shade changes. #999999 sits at roughly 2.3:1 against white, well under the 4.5:1 minimum; #595959 clears it with room to spare, at close to 7:1, without looking like pure black body text.
Common Mistakes
Mistake: “It looks readable to me, so the contrast is probably fine.” WCAG defines contrast as a specific, measured ratio, not a subjective impression. A combination that reads comfortably to someone with typical vision, on a calibrated monitor, in a dim room, can fail the 4.5:1 minimum outright: “looks fine to me” and “passes the math” are unrelated claims.
Mistake: “This text passes at 4.6:1, so it’s safe.” A ratio measured against the element’s default state doesn’t account for hover, focus, error, or dark-mode states that can shift either color. Text that passes when the page loads can drop below the minimum the moment a user hovers a link or triggers a validation error, a separate, state-dependent check covered by Text contrast fails after a hover, focus, or state change.
Mistake: “Large, bold headings don’t need to meet contrast requirements.” Large text gets a lower threshold (3:1 instead of 4.5:1) because bigger glyphs stay legible at less contrast, not because size exempts text from the rule. A 32px heading in pale grey on white can still fail 3:1 if the colors are close enough.
Mistake: “Placeholder text is meant to be lighter, so it’s exempt.” A placeholder is a real text node rendered inside the input, and WCAG makes no exception for it. Many CSS frameworks and browser default stylesheets set placeholder color well under 4.5:1 out of the box, which makes this one of the most common contrast failures that ships without anyone deliberately choosing the color.
How RedFlag Detects This
Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s color-contrast rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. For each text node, axe-core computes the relative luminance of the resolved foreground and background colors from computed styles, determines whether the text qualifies as “large” from its font size and weight, and compares the resulting ratio against the 4.5:1 or 3:1 threshold.
False negative: axe-core can’t reliably resolve a background color behind text sitting on a gradient, a background image, or a stack of semi-transparent overlapping elements; those cases surface as “incomplete” results, which RedFlag discards entirely since it only scans resultTypes: ['violations'], so contrast failures on photographic or gradient backgrounds are invisible to this check. False positive: rare, but possible when an element’s true rendered background comes from a layer axe-core’s computed-style lookup doesn’t account for, such as a ::before pseudo-element used purely for a background tint. Manual step: manually check contrast for any text over an image, gradient, or complex layered background, since these are exactly the cases the automated check can’t evaluate.
Manual Testing
- Open the page in Chrome or Firefox and open DevTools’ Elements panel.
- Click the color swatch next to a text element’s
colorvalue; DevTools shows the computed contrast ratio against its background directly in the color picker. - Confirm the displayed ratio meets 4.5:1 for normal text or 3:1 for large text (18.66px bold / 24px regular and up); DevTools flags a failing ratio with a warning icon.
- For text over an image or gradient, screenshot the actual rendered pixels and sample the darkest and lightest points behind the text with the WebAIM contrast checker’s eyedropper, since DevTools can’t resolve a non-solid background automatically.
- Repeat at the browser’s default zoom and at 200% zoom; reflowed layouts sometimes shift text onto a different background color than the one tested at 100%.
Related WCAG Success Criteria
1.4.3 Contrast (Minimum): Text and images of text must have a contrast ratio of at least 4.5:1 against their background, or 3:1 for large text. This rule is a direct, mechanical check of that exact ratio on rendered text.
1.4.11 Non-text Contrast: Interface components and graphical objects (button borders, form field outlines, icons) need at least 3:1 contrast against adjacent colors. This is a related but distinct requirement covering non-text elements (see UI component has insufficient contrast), which this rule’s automated check does not evaluate.
Related Issues
Text does not meet enhanced AAA contrast requirements is this rule’s stricter AAA sibling: the same measurement at a higher 7:1 / 4.5:1 bar, worth targeting once the AA baseline is solid.
Text contrast fails after a hover, focus, or state change covers the state-dependent failures this static, scan-time check can’t catch: text that passes on page load but drops below the minimum on hover or focus.
Link is not distinguishable from surrounding text is the color-only companion to this rule: a link relying purely on a color difference from body text, rather than an underline or other non-color cue, can pass raw contrast math and still be indistinguishable to a color-blind reader.
UI component has insufficient contrast extends the same contrast concept to borders, icons, and other non-text interface elements this rule’s color-contrast check doesn’t cover.
References
- W3C Understanding 1.4.3: Contrast (Minimum)
- W3C Technique G18: Ensuring that a contrast ratio of at least 4.5:1 exists
- WebAIM: Contrast and Color Accessibility
- WebAIM Contrast Checker
Frequently asked questions
What's the difference between the AA and AAA contrast requirements?
AA (1.4.3) requires 4.5:1 for normal text and 3:1 for large text, the level most laws and organizations require. AAA (1.4.6) raises that to 7:1 and 4.5:1, a stricter, "gold standard" bar most sites treat as a stretch goal rather than a hard requirement.
Does placeholder text need to meet the contrast requirement?
Yes. A placeholder is real text rendered inside the input, and WCAG treats it exactly like any other text: it must meet 4.5:1 against the input background. Many frameworks and CSS resets ship placeholder styles well below that ratio by default.
Do large, bold headings still need to meet a contrast minimum?
Yes, just a lower one. Text at 18.66px bold or 24px regular and above only needs 3:1 instead of 4.5:1, because larger glyphs stay legible at lower contrast; it isn't exempt from the requirement entirely.
Are logos and brand marks exempt from the contrast requirement?
Yes. WCAG explicitly excludes text that is part of a logo or brand name from 1.4.3, since brand identity often depends on fixed colors. This exemption does not extend to any other text on the page that happens to use brand colors.
Does axe-core catch contrast failures that only appear on hover or focus?
No. RedFlag's automated color-contrast check evaluates the page in its default, static state at scan time. A color combination that only fails after a hover, focus, or error state is a separate, human-reviewed check; see redflag-contrast-dynamic.