# Orange links, a hero headline and a hover state that are hard to read

Source: https://easeweb.dev/learn/text-contrast
Topics: Colour and contrast > Text has enough contrast (WCAG 1.4.3)
The fix: Use a darker shade of the brand colour for text and keep the bright one for decoration. Put text over images on a backing that holds the ratio over the lightest possible image. Make hover and focus states darker, not lighter, and measure them like the resting state.
Test with: contrast measurement, mouse, keyboard, forced colors
References: https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html https://www.w3.org/WAI/WCAG22/Techniques/general/G18 https://www.w3.org/WAI/WCAG22/Techniques/general/G145 https://webaim.org/resources/contrastchecker/ https://developer.chrome.com/docs/devtools/accessibility/contrast https://www.tpgi.com/color-contrast-checker/

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.

## What happens

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.

## Why it fails

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.

## Who is affected

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.

## What automation and AI miss

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.

## Before: the brand colour, a bare photo, and a paler hover

```css
: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 */
}
```

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.

## After: a text shade of the brand, a backing, and a darker hover

```css
: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;
}
```

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.

## Implementation decisions

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.

## How to measure contrast

**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

1. Measure each text colour against its rendered background at its real size and weight, with the WebAIM checker or the DevTools colour picker, and confirm 4.5:1, or 3:1 for large text.
2. For every text slot over an image or gradient, use the Colour Contrast Analyser's eyedropper to sample the text and then the lightest (or darkest) part behind it, across every image the slot can show.
3. Point at and Tab to each link and button, and measure the text in the hover and focus states, forcing each state from the DevTools `:hov` panel.
4. Switch to each theme the site offers, then turn on forced colors, and confirm the text is still readable.

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.
