/ โ† Accessibility Fixes / Colour Contrast
In this guide
  1. The contrast ratios WCAG sets
  2. The pairs that fail most often
  3. How to measure contrast yourself
  4. The fix, step by step
  5. Building a palette that passes by design
  6. Platform notes
  7. Contrast is not the only visual check
  8. Questions and answers

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.

WhatCriterion and levelMinimum ratio
Normal body text and labels1.4.3 Contrast (Minimum), AA4.5:1
Large text: at least 24px regular, or about 18.7px bold (18pt and 14pt)1.4.3, AA3:1
Boundaries of controls, such as input borders and checkbox outlines, and their states1.4.11 Non-text Contrast, AA3:1 against adjacent colours
Meaningful icons and parts of charts1.4.11, AA3:1 against adjacent colours
Enhanced text contrast1.4.6 Contrast (Enhanced), AAA7: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

Placeholder text is not exempt. If it is visible and conveys information, it must meet 4.5:1 like any other text.

๐Ÿ’ก You may have read about APCA, a newer contrast method being developed for a future WCAG 3. It is not part of WCAG 2.2, which is what checkers and most policies test against. Meet the WCAG 2 ratios first.

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.

PatternTypical colourRatioPassing alternative
Light grey body or caption text#9999992.85:1#767676 at 4.54:1 or darker
Placeholder and hint text#aaaaaa2.32:1#767676, or move hints out of the field
White text on a bright orange buttonbackground #ff6b352.84:1background #c2410c at 5.18:1
White text on a mid greenbackground #4caf502.78:1background #2e7d32 at 5.13:1
Pale input borders#d1d5db1.47:1a border of #8a8a8a or darker, 3.45:1
Muted grey text in a dark theme#6b7280 on #1118273.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.

โš ๏ธ Lowering 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

Focus outlines are covered in more detail in fixing focus indicators. For a broader walk through the checker's findings, see the Accessibility Guide.

Questions and answers

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.

Find every low contrast pair on your page

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 โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Accessibility Fixes  ·  Fixing Focus Indicators  ·  Accessibility in WordPress  ·  Accessibility Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.