MobileCheck

Low contrast text: WCAG contrast ratios and how to fix them

Updated October 17, 2026 · Covers: Text is hard to see against its background

What it means

Contrast is the difference in brightness between text and the background behind it. WCAG, the web accessibility standard, expresses it as a ratio from 1:1 (identical colours) to 21:1 (black on white). MobileCheck flags text that falls below the WCAG AA level:

The stricter AAA level asks for 7:1 and 4.5:1. AA is the level most laws and policies refer to and the one MobileCheck uses.

Why it matters

Low contrast is a problem for everyone on a phone, not only for people with low vision. Phones are used outdoors, at low brightness to save battery, through screen glare, and on displays that don't reproduce subtle greys well. Light-grey text that looks elegant on a calibrated monitor can simply vanish in sunlight.

Contrast also comes up in accessibility audits more than almost any other issue, and it is one of the easiest to fix: usually a colour value or two in your stylesheet.

How the contrast ratio is calculated

The ratio compares the relative luminance (perceived brightness) of the two colours: (lighter + 0.05) / (darker + 0.05). Hue doesn't count; only brightness does. That is why red text on green can fail even though the colours look very different.

Some reference points on a white background:

Text colourRatio on #ffffffNormal text (4.5:1)
#00000021:1Passes
#595959about 7:1Passes (AAA too)
#767676about 4.5:1Just passes
#777777about 4.48:1Just fails
#999999about 2.8:1Fails

You don't need to calculate this by hand. Chrome DevTools shows the contrast ratio when you click a colour swatch for text in the Styles panel, and Lighthouse includes a contrast audit. Any online colour contrast checker will test a pair for you.

How MobileCheck finds it

MobileCheck reads the computed text colour of each visible text element and works out the effective background by walking up through its ancestors, blending semi-transparent layers. It then applies the 4.5:1 or 3:1 threshold depending on the text's size and weight, and re-checks each candidate before reporting it. Failing elements are highlighted on the screenshot.

The check runs three times per device: in the normal view, with dark mode turned on (prefers-color-scheme: dark), and with the mobile menu opened. Text drawn over a background image or gradient is skipped, because its contrast changes from pixel to pixel and can't be measured reliably; check those spots by eye.

How to fix it

Darken the text or lighten the background

Most fixes are a single value. If your grey body text is #999, moving to #595959 or #666 keeps the soft look and passes comfortably:

body       { color: #222; background: #fff; }
.muted     { color: #595959; }   /* about 7:1 on white */
a          { color: #0b57d0; }   /* check brand colours too */

Brand colours are a frequent cause. A bright brand orange or light blue often works for buttons with dark text, or as a background, but fails as text on white. Keep the brand colour and pick a darker shade for text.

Large text

Headings and big buttons can use slightly lighter colours because the 3:1 threshold applies to them. Remember that "large" means 24px or 18.66px bold in CSS pixels. A 20px regular heading still needs 4.5:1.

Placeholder text

Placeholder text inside form fields is text, and WCAG's contrast guidance applies to it. Many designs use very pale placeholders. Darken them, and better still, put essential hints in a visible label instead of a placeholder that disappears when the user starts typing:

::placeholder { color: #6b6b6b; opacity: 1; }

Text in disabled controls and in logos is exempt from the contrast requirement.

Text on images

Text over photos may pass on one part of the image and fail on another, and phones crop hero images differently from desktops. Give the text a guaranteed background: a semi-transparent overlay, a gradient behind the text, or a solid panel:

.hero { position: relative; }
.hero::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgb(0 0 0 / 0.65), transparent 60%);
}
.hero-text { position: relative; color: #fff; }

Dark mode

Dark mode is where contrast problems hide, because few people test it. Common causes:

Define colours as custom properties and switch them together, so text and background always change as a pair:

:root { --bg: #fff; --text: #222; --muted: #595959; }
@media (prefers-color-scheme: dark) {
  :root { --bg: #121212; --text: #eaeaea; --muted: #a8a8a8; }
}
body { background: var(--bg); color: var(--text); }

The dark mode guide covers this in more detail.

How to check the fix

Run the free check again and compare the normal and dark-mode results; both should be free of contrast warnings. Then open the page in the phone window and look at areas MobileCheck can't measure, such as text over photos and sliders.

Fonts render differently in WebKit and Chromium, and thin weights can look lighter in one than the other. The ratio is the same, but thin text at a borderline ratio reads worse, so leave a margin above the minimum. If text is also small, fixing text size at the same time makes the biggest difference.

Frequently asked questions

What contrast ratio does WCAG require?

WCAG 2.2 level AA requires at least 4.5:1 for normal text and 3:1 for large text. Level AAA raises this to 7:1 and 4.5:1.

What counts as large text for contrast?

Text of at least 18pt, or at least 14pt when bold. In CSS pixels that is 24px regular or about 18.66px bold.

Does placeholder text need to meet contrast requirements?

Yes, placeholder text is still text people need to read, so it should meet the same ratio. Text in disabled controls and in logos is exempt.

Why does my site fail contrast only in dark mode?

Usually some text keeps a hard-coded dark colour while the background turns dark, or a dark theme misses some components. Switch text and background colours together, for example with CSS custom properties.

Related guides