Designers tend to know the basic rule: WCAG 2.2 AA requires 4.5:1 contrast for normal text and 3:1 for large text (≥18px, or 14px bold). What they don't always know is that the body text usually passes. The failures that show up in real audits are in the corners of the design — the hover state nobody tested, the placeholder pretending to be a label, the white text on a photograph that works on the hero crop but breaks on mobile.

Here are the cases I see fail repeatedly, in roughly the order I see them. For each one I'll cite the WCAG criterion that catches it and describe the fix that actually works in production.

1. Hover and active states that lighten

The default state of the button passes contrast at 4.7:1. On hover, the design system lightens the background by 10% to "indicate interactivity". On hover, the contrast drops to 3.2:1. The button now fails AA in its active interaction state — the moment when contrast matters most, because the user is committing to an action.

Why this happens: hover-as-lightening is a deeply ingrained convention from the print era of Microsoft Office and the early web. It treats hover as "secondary" to the primary state. But hover is when the user is most engaged and most needs visual confirmation.

Fix: on dark-on-light buttons, hover should usually darken the background, not lighten it. The instinct that says "lightening = active" is wrong; what you actually want is "more contrast = active". Test both default and hover with the contrast checker, not just default.

WCAG reference: 1.4.3 Contrast (Minimum) applies to text in all states.

2. Placeholder text used as a label

The form input has no visible label. The placeholder reads "Email address". When the user starts typing, the placeholder disappears, taking the only label with it. By the time they get to "Postcode" three fields later, they have no idea which field they're in.

This pattern fails on multiple criteria simultaneously:

Fix: persistent visible labels above every input. Reserve placeholders for example values ("e.g. you@studio.com") that don't carry the meaning of the field.

3. Text on imagery (the gradient that isn't there)

The hero image contains a soft sky and a complex landscape. The white text is positioned in the upper third where the sky is. The contrast against the sky is 5.2:1, the design passes review. The site goes live, and on a smaller screen the image crops differently — the text now overlaps the landscape, where contrast drops to 1.8:1. The user can't read the headline.

Variations of this:

Fix: three options, in increasing order of robustness:

  1. Solid scrim. A semi-transparent solid block behind the text. Works at all crops.
  2. Test the worst case. Pick the darkest pixel the text could ever overlap, and contrast-test against that.
  3. Don't put text over imagery at all. Put the text alongside or below. This is the move most premium publications have made.

WCAG reference: 1.4.3 Contrast (Minimum). Tools like our contrast checker handle the maths but can't tell you which pixel the text will overlap — that's still a designer call.

4. Disabled buttons (the "fine print" failure)

Disabled buttons are styled at 30% opacity to "communicate disabled-ness". The contrast against the button background is now 1.5:1. The button label is essentially unreadable.

This one is genuinely tricky. WCAG 1.4.3 has a famous exception: "Text or images of text that are part of an inactive user interface component… have no contrast requirement." So technically, disabled buttons can fail contrast and still pass AA.

But: just because it's technically compliant doesn't make it usable. A disabled button is information — it's telling the user "you can't do this thing yet". If the user can't read what the button says, they don't know what they're being prevented from doing.

Fix: aim for 3:1 on disabled state, even though it's not required. Or better — replace the disabled button entirely. If the user can't proceed, why is the button there? Often the better UX is an enabled button with a tooltip explaining the missing prerequisite.

5. Focus rings that disappear on light backgrounds

The design system specifies a focus ring of 2px solid blue. It looks great on the dark navigation. But on a white card with a light blue background, the focus ring blends in. Keyboard users lose their position the moment they tab into the card.

Variations:

WCAG reference: 2.4.7 Focus Visible (AA). New in 2.2: 2.4.13 Focus Appearance (AAA) which adds size and contrast minimums to the focus indicator itself.

Fix: use a focus indicator that combines outline + offset shadow + colour shift, so at least one of those modes is visible against any surrounding background. The Material 3 and Apple HIG guidelines for focus rings are both worth borrowing.

6. Charts and data visualisations

The line chart has six different lines colour-coded by category. They look great against the white background. They're indistinguishable for users with red-green colour deficiency. The legend says "blue line, red line, green line" which is meaningless if you can't tell the difference between blue, red and green.

WCAG reference: 1.4.1 Use of Color. This isn't strictly a contrast failure — it's a "colour-as-only-distinguisher" failure. Same family of problem, different criterion.

Fix: always pair colour with at least one other distinguisher. Line styles (solid/dashed/dotted), markers (circles/triangles/squares), or — best — direct labels on the lines themselves. Direct labels also remove the need for users to look back and forth between chart and legend, which is good for everyone.

7. Error messages that rely on red text

The form field is fine until the user submits. Then the field outline turns red, and red error text appears below. Users with colour deficiency see no change in the field outline. Users on a screen reader hear no announcement because the error wasn't wrapped in an aria-live region.

Three failures in one:

Fix: error icon next to the field (visual non-colour cue), error text wrapped in role="alert" or aria-live="polite", and the field's aria-describedby pointing to the error.

How to actually catch these in your process

Three habits worth building:

  1. Test all states, not just the default. When you contrast-check a button, also check hover, focus, active, disabled and any selected/pressed states.
  2. Test against the worst-case pixel. For text on imagery, pick the darkest or lightest spot the text could ever overlap and test that. Don't test the average.
  3. Run a check before handover. Once components are built, run the contrast checker on each variant. Ours covers the maths in seconds.

Most contrast failures aren't ignorance — they're the consequence of testing one happy path and assuming everything else inherits it. The audits catch the corners. Build the corner-checking into your handover ritual and most of these stop happening.


JP
Jamie Pow
Associate Director, Experience Design at JD.com · Previously Head of UX at Selfridges & Co · Building UX Companion