◎Home
← All posts

Why AI-Generated Websites Fail Contrast Checks (and How to Fix Them)

2026-10-03 · zhoujingkang · Founder

The community has gotten good at spotting AI-built websites. The font gives it away, the purple gradient gives it away, the card-inside-a-card layout gives it away — and there is a whole genre of posts collecting these tells. But all of those tells share one property: you can see them in a screenshot. There is exactly one tell you cannot see in a screenshot, it is the one that locks real users out of the content, and AI-generated sites fail it constantly: contrast. This article explains why the model gets color pairs wrong, what the numbers look like, and the token-level fix that holds.

The tells everyone can already see

Collect the current list and it reads like a field guide: the same default font stack, a purple-to-blue gradient nobody chose on purpose, cards floating on cards, rounded corners everywhere, a hero section that could belong to any of ten thousand landing pages. These are real tells and worth fixing — a distinctive typeface and an intentional layout take an afternoon.

What the lists have in common is that every item is visible. You can spot them in a screenshot, in a portfolio review, in the two seconds before you close the tab. That is also why they get fixed: what can be seen gets compared, and what gets compared improves.

The tell a screenshot cannot show

Contrast is measured between a text color and the specific background it sits on — not in isolation. That pairing is invisible in a screenshot, and it is where AI-generated pages fail most. A grey that looked fine on the white preview is dropped onto a tinted surface, and the pair quietly drops below the 4.5:1 threshold that WCAG 2.1 requires for body text.

The numbers make it concrete. Muted warm grey #A89890 on a soft off-white #FFF7F3 looks like a calm, tasteful combination — it measures 2.63:1, well below the 4.5:1 requirement. The classic grey-on-white fails too: #999999 on #FFFFFF is 2.85:1. Put #6B7280 on a hot pink #EC4899 accent and the pair collapses to 1.37:1 — text that is effectively unreadable. None of this is visible at a glance; every one of these pairs passes what UX people call the glance test.

This is why low contrast keeps winning WebAIM's annual analysis of the top million home pages: roughly four in five have detectable low-contrast text, year after year. Humans eyeball; the ratio does not care what the eye adapted to.

Why the model gets pairs wrong

An AI coding agent generates elements one at a time: a section, then its text, then its background. It has no persistent notion of a pair, so nothing forces the muted text it just emitted to be measured against the surface it is about to sit on. Each choice is locally plausible and globally unverified — the failure mode is not one bad color, it is the absence of any step that checks combinations.

The second problem is the training distribution. Asked for a modern-looking site, the model drifts toward the median of everything it has seen: the same greys, the same accent, the same muted-on-tinted pattern. A palette that is average across millions of pages is not checked against anything — average is not a standard.

And the combinatorics are brutal. A page with eight text roles and five surfaces has dozens of pairings; nobody verifies dozens of ratios by hand per screen, so the pairs that never got checked are exactly the muted ones that fail.

The fix: roles with measured pairs

The working approach is to stop choosing colors per element and start assigning roles, measured once. A seed color — your brand hex — expands into a fixed set of roles: background, surface, on-surface (body text), primary, on-primary (text on primary), muted, border, and the state colors. Each text role is measured against each surface role it can appear on, once, at definition time.

Once the roles hold, the agent's job changes from inventing colors to picking them. It cannot emit a muted that is not in the file, and every muted in the file has already met its surface at 4.5:1 or better. Dark mode uses the same role names with a second measured set — the dark variants fail differently (near-black pairings distort WCAG ratios), so they get their own pass.

This is what a semantic token system is for, and it is why the Brand Theme Engine derives the whole role set from one color and runs every text/background pair through WCAG 2.1 and APCA before anything is exported.

Five pairs to check before you ship

If you are auditing an AI-built page today, these are the pairs that fail most often. Measure each once, fix the role, and the whole class of failure disappears — fixing the role once means every page that consumes it inherits the fix:

  • Body text on page background — the easy one; AI usually gets this right (near-black on white measures 17.49:1).
  • Muted or secondary text on the page background — the classic failure; #999999 on white is 2.85:1 and passes the glance test.
  • Muted text on cards and tinted surfaces — the sneaky one; #A89890 on #FFF7F3 is 2.63:1.
  • Text on colored accents — the disaster case; #6B7280 on #EC4899 is 1.37:1. Text on a saturated accent needs its own measured on-accent role.
  • Brand color used as text — a brand orange like #EA580C on white is 3.56:1, fine for large text, below the 4.5:1 body threshold; the deep variant #9A3412 measures 7.31:1 and carries text instead.

Continue with a tool

References

FAQ

Do all AI-generated websites fail contrast checks?

No — body text on white usually passes. The failures cluster in the pairs nobody measures: muted text on tinted surfaces, text on saturated accents, and anything a screenshot cannot reveal.

What contrast ratio do muted text pairs need?

The same as any body text: 4.5:1 against WCAG 2.1 AA. Muted is a size and emphasis choice, not an exemption — #999999 on white at 2.85:1 fails regardless of how intentional it looks.

Does dark mode make contrast failures worse?

Different, not necessarily worse. Near-black pairings can distort WCAG ratios, so dark themes need their own measured set rather than an inverted copy of the light theme.

Try it on your own brand color

Paste one hex into the Brand Theme Engine and get a full light & dark web theme — semantic tokens, contrast-checked, export-ready.

Open the Brand Theme Engine →