Orange links, a hero headline and a hover state that are hard to read
Text is drawn in colours too close to what is behind it: a bright brand colour, a light photo, or a paler hover state.
The problem
People with low vision can't read the menu, the headline over the photo, or a button while they point at it.
A bakery's site uses a bright orange as its brand colour. The menu links and the prices are set in that orange on white. The home page opens with a photo of a cream-coloured shop front, and a white headline and subline sit straight on top of it. Further down, an "Order now" button has white text on a dark orange that reads well, until the pointer is over it: the hover state switches to a paler orange, and the words almost vanish just as someone is about to click.
Each piece of text is drawn in a colour too close to the one behind it. The orange #e8762c on white is 2.97:1. The white headline over the cream wall #f6e7d8 is 1.21:1. The button's hover state puts white on #f0954f, at 2.30:1. WCAG 1.4.3 asks for at least 4.5:1 for normal text, and 3:1 for large text: 24px and up, or 18.66px and up when bold. The headline is 32px bold, so it needs 3:1; the subline, the links and the button label are normal text and need 4.5:1. None of them gets there.
People with low vision, and many older readers, lose faint text first: letters blur into the background and words have to be guessed. People with colour vision deficiencies see some hues as closer than they look to others. Anyone reading on a phone in sunlight, on a dim laptop, or through screen magnification meets the same problem. The hover failure hits mouse users at the worst moment, when they need to confirm what they are about to choose.
Scanners such as axe check text at rest against a solid background colour. Over an image they usually report "needs review", or nothing, because the colour behind each letter depends on the photo, and an editor can change that photo tomorrow. They don't hover or focus anything, so a hover state that fades the text is never measured. AI tools tend to pick the brand colour for links because it matches the design, and to check a mock-up against its average background, not its lightest spot.
Try it
Read the menu and the headline, then point at or Tab to the button.
Can you read every word, including the button while you hover over it or focus it?
Baked this morning
Sourdough, buns and seasonal tarts.
A recreated demo. The broken version is intentionally inaccessible.
The fix
Give every piece of text 4.5:1 against what is really behind it, in every state.
- 1Normal text needs 4.5:1; text 24px and up, or 18.66px bold and up, needs 3:1.
- 2Measure text over an image against the lightest part it can land on, and give it a backing.
- 3Hover and focus need the same ratio as the resting state, so darken rather than lighten.
:root { --brand: #e8762c; /* 2.97:1 on white */}.nav a,.price { color: var(--brand);}.hero-text { color: #fff; /* 1.21:1 over the cream wall */}.button { background: #b4501a; /* white text: 5.12:1 */ color: #fff;}.button:hover,.button:focus-visible { background: #f0954f; /* white text: 2.30:1 */}:root { --brand: #e8762c; /* decoration only */ --brand-text: #b4501a; /* 5.12:1 on white */ --brand-strong: #8a3c0d; /* white text: 7.68:1 */}.nav a,.price { color: var(--brand-text);}.hero-text { color: #fff; /* White text: 5.74:1 even if the photo is pure white */ background: rgb(0 0 0 / 0.6); padding: 1rem 1.25rem;}.button { background: var(--brand-text); color: #fff;}.button:hover,.button:focus-visible { background: var(--brand-strong); text-decoration: underline;}Every colour here was picked to look right in the design file. The orange is the brand, the white headline looks good over the dark part of the photo, and the paler hover feels like a light touch. But none of them was measured against what is really behind the text in the state people see.
The links and prices now use a darker shade of the same orange, at 5.12:1. The headline sits on a 60% black backing; over the lightest possible photo, pure white, that backing is #666666 and white text on it is 5.74:1, so no photo can break it. The hover state gets darker, not lighter, at 7.68:1, and the underline gives a second cue that doesn't depend on colour at all.
Keep the brand colour, but give it a job it can do. A bright orange that fails as text can still fill a large shape, mark a decorative rule, or carry dark text on top of it. Put the text shade into its own token, so nobody has to remember which orange is safe for words.
For text over images, measure the worst case, not the average. A backing panel, a gradient scrim under the text, or darkening the photo itself all work. The backing approach is the most robust, because it doesn't depend on which image an editor uploads. A thin text shadow rarely reaches the ratio on its own; if you rely on one, measure the colour actually behind each letter.
Hover, focus, active, visited and selected states all count. Check each one that changes a colour. Watch for opacity on text and for semi-transparent text colours: the computed result is lighter than the token suggests. Each theme the site offers, dark mode included, needs its own measurement, since a pairing that passes on white can fail on grey.
Some text is exempt: logos, text in disabled controls, and text that is purely decorative or not visible. Placeholder text is not exempt, and is often the palest text on a page. In forced colors, the browser replaces text and background colours with the user's own, and Chromium draws a backplate behind text over images; check the hero there rather than assume.
The WebAIM Contrast Checker is the quickest way to test a pair of colours you already know. Enter the text colour as the foreground and the colour behind it as the background, as hex values. Read the two WCAG AA results: "Normal Text" is the 4.5:1 check, and "Large Text" is the 3:1 check. If a pair fails, move the Lightness slider under the foreground until both AA results pass. That is how a text shade such as #b4501a is found from the brand's #e8762c. The checker doesn't take transparency, so enter the colour that is actually rendered: copy it from the computed style in DevTools, or pick it from the screen.
Browser DevTools measure the colour in place. In Chrome or Edge, inspect the text, and in the Styles pane click the colour swatch next to color. The colour picker shows the contrast ratio against the background it detects, marks AA and AAA, and can suggest a colour that passes. To measure hover and focus, open the :hov panel, tick :hover or :focus-visible, and click the swatch again. Firefox's Accessibility Inspector reports contrast too. Neither tool can tell which part of a photo is behind each letter, so don't trust its number for text over images.
The Colour Contrast Analyser (CCA), a free desktop app from TPGi, has an eyedropper that picks any pixel on the screen. Use it for text over images: pick the text colour, then the lightest pixel behind the letters (the darkest, for dark text). Repeat for every image the slot can show.
Verify the fix
4 checks.
Test with: Contrast measurement, Mouse, Keyboard, Forced colors
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the colour tokens, the theme or the hero images change.
Limits
This is a starting pattern, not a guarantee for every application. Colour values and token names are examples; measure your own palette on your real backgrounds. A contrast ratio is a minimum, not proof that text is comfortable to read: thin weights, small sizes and long lines make reading harder at the same ratio. Check the complete pages with people who rely on it.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.