Fixing Colour Contrast Failures
Low contrast text is the accessibility problem automated checkers find most often, and it affects far more people than many owners expect: anyone with low vision or colour blindness, older visitors, and everyone reading a phone outdoors. Pale grey body text, white text on a bright brand colour and faint placeholder text are the usual culprits.
This guide explains the ratios WCAG sets for text and for interface parts, how to measure a pair of colours yourself, and how to adjust your palette so it passes without abandoning your brand.
The contrast ratios WCAG sets
Contrast is expressed as a ratio between the relative luminance, roughly the brightness, of two colours. It runs from 1:1, the same colour on itself, to 21:1, black on white. Hue does not enter the calculation, which is why a red and a green of similar brightness can look different to most people and still fail.
| What | Criterion and level | Minimum ratio |
|---|---|---|
| Normal body text and labels | 1.4.3 Contrast (Minimum), AA | 4.5:1 |
| Large text: at least 24px regular, or about 18.7px bold (18pt and 14pt) | 1.4.3, AA | 3:1 |
| Boundaries of controls, such as input borders and checkbox outlines, and their states | 1.4.11 Non-text Contrast, AA | 3:1 against adjacent colours |
| Meaningful icons and parts of charts | 1.4.11, AA | 3:1 against adjacent colours |
| Enhanced text contrast | 1.4.6 Contrast (Enhanced), AAA | 7:1 normal, 4.5:1 large |
WCAG does not allow rounding. A pair measuring 4.48:1 fails 4.5:1, even though it looks nearly identical
to a passing pair. Grey #777777 on white is 4.48:1 and fails; #767676 on white
is 4.54:1 and passes.
What is exempt
- Text in logos and brand names.
- Text and controls that are disabled and cannot be used.
- Purely decorative text, and text that is part of a photo with other significant visual content, such as a shop sign in a street scene.
- Text that is invisible to everyone.
Placeholder text is not exempt. If it is visible and conveys information, it must meet 4.5:1 like any other text.
The pairs that fail most often
A handful of patterns account for most contrast findings on small business sites. Ratios below are measured on a white background.
| Pattern | Typical colour | Ratio | Passing alternative |
|---|---|---|---|
| Light grey body or caption text | #999999 | 2.85:1 | #767676
at 4.54:1 or darker |
| Placeholder and hint text | #aaaaaa | 2.32:1 | #767676,
or move hints out of the field |
| White text on a bright orange button | background #ff6b35 | 2.84:1 | background
#c2410c at 5.18:1 |
| White text on a mid green | background #4caf50 | 2.78:1 | background
#2e7d32 at 5.13:1 |
| Pale input borders | #d1d5db | 1.47:1 | a border of #8a8a8a or darker, 3.45:1 |
| Muted grey text in a dark theme | #6b7280 on #111827 | 3.67:1 | #9ca3af
on #111827 at 6.99:1 |
The last row is worth noticing: #6b7280 passes on white at 4.83:1 but fails on a near-black
background. A colour does not pass or fail on its own; only a pair does.
How to measure contrast yourself
In the browser
In Chrome and Edge, inspect a piece of text, then click the small colour swatch next to its color
value in the Styles pane. The colour picker shows the contrast ratio against the background it detects,
with pass or fail marks for AA and AAA, and draws lines on the colour area showing where the thresholds
fall. Dragging the colour below the line gives you a passing shade immediately. Firefox's Accessibility
Inspector can also check contrast for the selected element and scan a page for contrast issues.
With a calculator
Any contrast calculator that follows the WCAG formula will do: enter the foreground and background hex codes. This is useful for brand guidelines and design files before anything is built.
When the background is not a flat colour
Checkers struggle with gradients, background images, semi-transparent overlays and text positioned over other elements. For these, measure against the lightest part of the background behind light text, or the darkest part behind dark text. If the text still passes at its worst point, it passes.
The formula, for developers
// Relative luminance of an sRGB colour, then the WCAG ratio
function lum(hex) {
const [r, g, b] = [0, 2, 4].map(i => {
const c = parseInt(hex.slice(i + 1, i + 3), 16) / 255;
return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}
function ratio(a, b) {
const [hi, lo] = [lum(a), lum(b)].sort((x, y) => y - x);
return (hi + 0.05) / (lo + 0.05);
}
ratio('#767676', '#ffffff'); // about 4.54
The fix, step by step
Step 1
Collect the failing colour pairs
Run the Accessibility Audit on the home page and on one page of each template, and list each contrast finding with its foreground colour, background colour and where it appears. You will usually find the same few pairs repeated many times, such as grey body text, a button style and footer links. Record each unique pair once, with the CSS selector or theme setting that produces it, so you fix causes rather than chasing individual instances around the site.
Step 2
Measure each pair against the right ratio
For every unique pair, decide which threshold applies: 4.5:1 for normal text, 3:1 for text at least 24px regular or about 18.7px bold, and 3:1 for input borders, icons and other interface parts. Measure each with the browser colour picker or a contrast calculator, and never round up. Note how far each pair is from passing, because a pair at 4.3:1 needs a small nudge while one at 2.3:1 needs a clearly different colour.
Step 3
Create accessible shades of brand colours
Keep the brand hue and change its lightness. Using the colour picker or a calculator, darken the colour step by step until white text on it reaches 4.5:1, or until it reaches 4.5:1 as text on white. Save this as a named variant, for example brand text or brand button, next to the original. Keep the bright original for large headings, decorative shapes and illustrations, where lower contrast is allowed, so the site still looks like your brand.
Step 4
Update colours once at the source
Change
the colour where it is defined rather than overriding it element by element. On a custom site this means
CSS custom properties or design tokens, such as --text-muted or --button-bg.
In WordPress block themes use Global Styles or theme.json; in a classic theme use the Customizer
or child theme stylesheet. In Shopify, change the colour scheme settings in the theme editor. Search
the code for hard-coded hex values that bypass the variables.
Step 5
Fix text placed over images
For headings and buttons laid over photos or videos, add a semi-transparent dark layer between the image and the text, or place the text on a solid panel. Test the ratio against the lightest area the text can cover, including on narrow screens where the crop changes. Where the text is baked into the image file itself, replace it with real text over the image so you control its colour, size and contrast, and so it can be translated and resized.
Step 6
Check borders, icons and every state
Check the parts people forget: form field borders, checkbox and radio outlines, icon-only buttons, chart lines, focus outlines and the selected state of tabs or filters. These need 3:1 against the colours next to them. Then check hover, focus, visited, active and error states for text contrast, because a link that turns pale grey on hover, or error text in a light red, is a separate failure even when the default state passes.
Step 7
Retest in light, dark and zoomed views
Run the Accessibility Audit again on every template you changed. If your site offers a
dark theme or follows the visitor's system setting with prefers-color-scheme, switch it
on and test again, because colours that pass on white often fail on near-black. Zoom to 200 percent and
check that text has not reflowed onto a lower contrast part of a background image. Finally, look at the
key pages on a phone outdoors or at low brightness.
Building a palette that passes by design
The lasting fix is a colour system where every text colour is paired with the backgrounds it may appear on, and each pairing is known to pass. A small set of named variables is enough for most sites:
:root {
--bg: #ffffff;
--text: #1f2937; /* about 14.7:1 on white */
--text-muted: #6b7280; /* 4.83:1 on white */
--brand: #ff6b35; /* decorative and large text only */
--brand-strong: #c2410c; /* buttons with white text, 5.18:1 */
--border-input: #8a8a8a; /* 3.45:1 on white */
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #111827;
--text: #f3f4f6;
--text-muted: #9ca3af; /* 6.99:1 on #111827 */
--border-input: #9ca3af;
}
}
.btn-primary { background: var(--brand-strong); color: #ffffff; }
input, select, textarea { border: 1px solid var(--border-input); }
Write the approved pairs into your brand guidelines, with their ratios, so designers, developers and agencies all use the same ones. When a new colour is proposed, it is checked against the backgrounds it will sit on before it is used.
opacity on text, or using a semi-transparent text colour
such as rgba(0,0,0,0.4), reduces contrast in a way that is easy to miss in the code. Measure
the colour as it actually renders.Platform notes
WordPress. Block themes define their palette in theme.json and expose
it in Appearance, Editor, Styles. Change the palette there and blocks that use palette colours update
everywhere. Blocks where an editor picked a custom colour keep that colour, so search for them after
changing the palette. The block editor warns when an editor picks a low contrast text and background
combination in some blocks; treat that warning seriously. Page builders keep their own global colour
settings, which need changing separately.
Shopify. Most current themes use colour schemes set in the theme editor, each with a background, text, button and button label colour. Fix the scheme rather than individual sections. Apps that add announcement bars, pop-ups and review widgets usually have their own colour settings in the app.
Custom and framework sites. Utility frameworks such as Tailwind make it easy to pick grey shades by number; agree which shades are allowed for text on which backgrounds. Automated accessibility tests in your test suite can catch many contrast regressions in components before they ship, though not text over images.
Contrast is not the only visual check
- Do not rely on colour alone. WCAG 1.4.1, Use of Color, at level A, means information shown by colour must also be available another way. Links within paragraphs are usually underlined for this reason, and error fields need an icon or message as well as a red border.
- Text size and weight. Very thin font weights look fainter than their measured ratio suggests. If a light weight reads poorly, use a heavier weight or darker colour even if it technically passes.
- Visitors who change colours. Some people use Windows contrast themes or browser extensions that override colours. Test that your site still works in forced colours mode, particularly icons and focus outlines drawn with backgrounds or shadows.
Focus outlines are covered in more detail in fixing focus indicators. For a broader walk through the checker's findings, see the Accessibility Guide.
1. What contrast ratio do I need for WCAG AA?
At least 4.5 to 1 between normal text and its background, and at least 3 to 1 for large text, meaning roughly 24 pixels regular or about 18.7 pixels bold. Interface parts such as input borders, checkbox outlines, meaningful icons and focus indicators need at least 3 to 1 against the colours next to them. These are minimums with no rounding, so 4.48 to 1 is a failure. Level AAA asks for 7 to 1 for normal text, but that is not expected across whole sites.
2. Does my logo have to meet contrast requirements?
No. WCAG exempts text that is part of a logo or brand name from the contrast minimum, so you do not need to redesign your logo. The exemption covers the logo itself, not everything in your brand colour. A tagline written in live text beside the logo, navigation links in brand orange or a button using the logo colour still need to meet the normal ratios. That is why most accessible brand palettes include a darker variant of the main colour for text and buttons.
3. Why does the checker fail a colour that looks fine to me?
Contrast that looks acceptable on a good monitor in a dim room can be hard to read for someone with low vision, on a cheap screen, or on a phone in sunlight. The ratio is a measured value, so it removes personal judgement. Another common reason is that the checker measured a state or background you did not look at, such as the hover colour, the text colour inside a disabled-looking button that is actually active, or a lighter part of an image.
4. Do placeholder text and disabled buttons need good contrast?
Placeholder text does, because it is visible text that conveys information, so it must reach 4.5 to 1. Many default browser placeholder colours are too light, and a common better approach is to put hints above the field as real text. Disabled controls are exempt, because they cannot be used. Be careful, though: a button styled pale grey to look disabled when it can actually be pressed is not exempt, and many sites do this with submit buttons.
5. How do I check contrast for text over a photo?
Measure against the part of the photo that gives the worst result: the lightest area behind white text, or the darkest area behind dark text. Browser tools and checkers often cannot sample an image correctly, so use a colour picker on the actual pixels. Remember that the crop changes on different screen sizes. The reliable fix is a dark overlay or a solid panel behind the text, which makes the result predictable whatever the photo underneath looks like.
6. Is contrast different in dark mode?
The rules are the same, but the results change because every pair is new. Grey text that passes on white can fail on a near-black background, and a brand colour chosen for white text may be too dark to read on black. Treat dark mode as a second palette and measure each text and background pair in it. Also avoid pure white text on pure black over large areas, which some readers find causes glare, and pick slightly softer shades that still pass comfortably.
7. Can an accessibility widget that changes contrast fix my site?
A toolbar that lets visitors switch to a high contrast mode does not make your default design meet WCAG, because the standard applies to the page as delivered, and many people who need better contrast will never find or use the widget. It also cannot fix text inside images. Fix the colours in your stylesheet or theme settings so the default design passes. Visitors who need more can already use their own browser and operating system settings.
The Accessibility Audit tests your page against WCAG 2.2 level AA and lists each finding, including text with too little contrast against its background, with the element that failed and the success criterion behind it.
Open Accessibility Fixes โ