Home
Blog
Top 10 EAA Violations We Find on European Websites — and How to Fix Them in an Afternoon

Back to Blog

Best Practices

Top 10 EAA Violations We Find on European Websites — and How to Fix Them in an Afternoon

Ten WCAG 2.1 AA failures that show up on most EU sites, with copy-paste fixes. Fix these and you address most automated-detectable EAA findings.

Updated 16 May 2026

14 min read

Also in:

EN
DENLFR
Most Common WCAG Failures on EU Websites (WebAIM Million 2024)Low contrast text81% of sitesMissing image alt text54.5% of sitesMissing form labels48.6% of sitesEmpty links44.6% of sitesEmpty buttons27.5% of sitesMissing document lang17.1% of sitesSource: WebAIM Million 2024 Annual Accessibility Analysis — webaim.org/projects/million

Based on the WebAIM Million 2024 Annual Accessibility Analysis of the top 1 million homepages. These six failure types account for over 96% of all detected WCAG errors. The violations below map directly to these categories.

How we ranked them

Order is by prevalence × legal risk, not raw frequency. A violation that is everywhere but rarely cited (e.g. minor heading skips) ranks lower than something less common but routinely flagged in complaints (e.g. unlabelled form fields on a checkout page).

According to the WebAIM Million 2024 report, these six failure types alone appear on over 96% of all pages with detected errors — giving them the highest signal-to-noise ratio in both audits and enforcement actions.

For each, you get:

  • The WCAG 2.1 AA criterion it fails
  • The axe-core rule that detects it
  • Why it gets you a complaint
  • The fix, with code

Let's go.

1. Missing alt text on meaningful images

WCAG: 1.1.1 Non-text Content (Level A) · axe rule: image-alt

This is the single most-cited finding in WCAG audits and the easiest to fix.

The mistake almost everyone makes: leaving alt off entirely on a decorative image, then leaving it off on a meaningful image too. Screen readers then announce nothing for the meaningful image — and the user has no idea there's a product photo, a chart, or a CAPTCHA.

<!-- ❌ Bad: meaningful image with no alt -->
<img src="/products/red-chair.jpg">

<!-- ✅ Good: meaningful image, descriptive alt -->
<img src="/products/red-chair.jpg" alt="Red leather armchair, mid-century style, side view">

<!-- ✅ Good: decorative image, empty alt is correct -->
<img src="/decorative/swoosh.png" alt="">

Important nuance: alt="" is the right answer for genuinely decorative images. It is not the right answer for "I don't know what to write." A missing description on a product image will cost you a complaint; an empty alt on a decoration is the spec-correct choice.

2. Insufficient text-to-background contrast

WCAG: 1.4.3 Contrast (Minimum) (Level AA) · axe rule: color-contrast

You need a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (≥18pt or ≥14pt bold). This is the most common AA failure on landing pages — light grey text on white backgrounds, brand-coloured buttons that do not pass against the background.

How to check fast: paste your foreground/background hex into WebAIM's contrast checker or your browser devtools (Inspect → Accessibility tab in Chrome/Firefox).

/* ❌ Bad: 2.6:1 ratio, fails AA */
.muted { color: #aaaaaa; background: #ffffff; }

/* ✅ Good: 4.6:1 ratio, passes AA */
.muted { color: #767676; background: #ffffff; }

/* ✅ Good for placeholders and disabled states (still readable) */
.disabled { color: #595959; background: #ffffff; } /* 7:1 */

Contrarian note: many design systems (Material, Apple HIG, Tailwind defaults) ship gray-300, gray-400 as common text colors against white. Both fail AA. Audit your design tokens once, not your pages.

3. Form inputs without programmatic labels

WCAG: 1.3.1 Info and Relationships (Level A), 4.1.2 Name, Role, Value (Level A) · axe rule: label

This is the highest-legal-risk violation on e-commerce sites. A checkout form where screen readers cannot announce "Email", "Postal code", "CVV" — that's the kind of failure that triggers French enforcement actions like the November 2025 lawsuits against Carrefour and Auchan (see France's enforcement data).

<!-- ❌ Bad: visual placeholder, no label -->
<input type="email" placeholder="Email address">

<!-- ❌ Bad: label not associated -->
<label>Email</label>
<input type="email">

<!-- ✅ Good: explicit label association -->
<label for="email">Email</label>
<input id="email" type="email">

<!-- ✅ Good: aria-label when visual label is impractical (e.g. icon-only) -->
<input type="search" aria-label="Search products">

<!-- ✅ Good: aria-labelledby pointing to existing visible text -->
<span id="zip-label">Postal code</span>
<input aria-labelledby="zip-label">

placeholder is never a substitute for a label. It disappears when the user starts typing.

4. Missing skip-to-content link

WCAG: 2.4.1 Bypass Blocks (Level A) · axe rule: skip-link

Keyboard-only and screen-reader users have to tab through every navigation link before reaching the main content — on every page. A skip link lets them jump past it.

<!-- Place this as the very first focusable element in <body> -->
<a href="#main" class="skip-link">Skip to main content</a>
<!-- ... -->
<main id="main">...</main>
/* Visually hide until focused — never use display:none */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
}
.skip-link:focus {
  left: 0;
  background: #000;
  color: #fff;
  padding: 0.75rem 1rem;
  z-index: 9999;
}

If you use Next.js / Nuxt / SvelteKit, this is one component imported into your root layout. Five minutes, fixes one of the most-flagged violations.

5. Empty links and buttons

WCAG: 2.4.4 Link Purpose (Level A), 4.1.2 Name, Role, Value (Level A) · axe rules: link-name, button-name

Icon-only buttons and links — the hamburger menu, the search icon, the close-X on modals — frequently ship without an accessible name. Screen readers announce "button" with no clue what it does.

<!-- ❌ Bad: icon button, no name -->
<button><svg>...</svg></button>

<!-- ✅ Good: aria-label provides the name -->
<button aria-label="Open navigation menu">
  <svg aria-hidden="true">...</svg>
</button>

<!-- ✅ Good: visually-hidden text for screen readers -->
<button>
  <svg aria-hidden="true">...</svg>
  <span class="sr-only">Open navigation menu</span>
</button>

The aria-hidden="true" on the SVG matters — without it, the screen reader may announce both the SVG (often as "graphic") and the label, which is noisy.

6. Missing language attribute on <html>

WCAG: 3.1.1 Language of Page (Level A) · axe rule: html-has-lang

Without lang, screen readers do not know which pronunciation rules to use — French content gets read in English phonetics, German in French, and the result is unintelligible.

<!-- ❌ Bad -->
<html>

<!-- ✅ Good -->
<html lang="en">

<!-- ✅ Good for multilingual content with a primary language -->
<html lang="de">
  <body>
    <p>The German term <span lang="en">"deadline"</span> is widely used.</p>
  </body>
</html>

This is one attribute. There is no excuse for missing it.

7. Broken heading hierarchy

WCAG: 1.3.1 Info and Relationships (Level A) · axe rule: heading-order

Screen reader users navigate by headings — "next heading" is one of the most-used commands. If your page has an h1, then jumps straight to h4, skipping h2 and h3, the hierarchy is broken and navigation fails.

<!-- ❌ Bad: h1 → h4 → h2 -->
<h1>Page title</h1>
<h4>Subsection</h4>
<h2>Another section</h2>

<!-- ✅ Good: monotonic, no skipped levels -->
<h1>Page title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Next section</h2>

A common cause is using heading levels for visual styling. Don't. Use the correct level for structure, then style with CSS.

8. Interactive elements that aren't keyboard accessible

WCAG: 2.1.1 Keyboard (Level A) · axe rule: keyboard (manual + automated checks)

Every clickable element must be reachable and operable via keyboard alone. The most common failure: using <div onclick=...> instead of <button>.

<!-- ❌ Bad: not keyboard-focusable, not announced as button -->
<div class="btn" onclick="doThing()">Click me</div>

<!-- ✅ Good: native semantics give keyboard + screen reader for free -->
<button type="button" onclick="doThing()">Click me</button>

<!-- ✅ Good: if you absolutely must use a div, you have to add all of this -->
<div
  role="button"
  tabindex="0"
  onclick="doThing()"
  onkeydown="if(event.key==='Enter'||event.key===' '){doThing()}"
>
  Click me
</div>

The third option is almost always wrong. Use a <button>. The semantic element wins on every metric — accessibility, keyboard, focus styles, screen reader announcement, browser autofill.

9. Modals without focus management

WCAG: 2.4.3 Focus Order (Level A), 2.1.2 No Keyboard Trap (Level A) · partial automation, mostly manual

A modal that does not trap focus is a frequent cause of "I opened the modal and then couldn't get out." When a modal opens, focus must move into the modal; while it's open, Tab should cycle within it; on close, focus returns to the trigger.

The right way in 2026 is the native <dialog> element:

<!-- ✅ Good: native dialog with showModal() handles focus trap automatically -->
<button type="button" onclick="document.getElementById('confirm-dialog').showModal()">
  Delete account
</button>

<dialog id="confirm-dialog" aria-labelledby="dialog-title">
  <h2 id="dialog-title">Confirm deletion</h2>
  <p>This cannot be undone.</p>
  <form method="dialog">
    <button value="cancel">Cancel</button>
    <button value="confirm">Delete</button>
  </form>
</dialog>

For React/Vue/Svelte component libraries, verify your modal component supports aria-modal, focus trap, and ESC-to-close. The popular ones (Radix UI, Headless UI, Mantine Modal, MUI Dialog) all do — but custom modals or older third-party widgets often don't.

10. Cookie banners that block everything

WCAG: 2.1.1 Keyboard (Level A), 1.4.13 Content on Hover or Focus (Level AA), 4.1.2 · partial automation

This is the most user-visible failure on European sites because GDPR has forced cookie banners onto every site, and most banner implementations are accessibility disasters:

  • Tab order traps the user inside the banner forever
  • Buttons are unlabelled or labelled with vague text ("Accept all" without context)
  • The banner overlays the main content but doesn't carry the appropriate role / aria-modal
  • Screen reader users can't dismiss it

The fix:

<!-- ✅ Good: dialog role, accessible name, focus management -->
<div role="dialog" aria-modal="true" aria-labelledby="cookie-title">
  <h2 id="cookie-title">Cookie consent</h2>
  <p>We use cookies for analytics. You can accept all, reject non-essential, or customise.</p>
  <button>Accept all</button>
  <button>Reject non-essential</button>
  <button>Customise</button>
</div>

If you use a CMP (Consent Management Platform) like Cookiebot, OneTrust, or Iubenda — verify they pass WCAG. Most of the big ones do in their default configuration; many self-built banners do not. One of the most common French enforcement issues in late 2025 was inaccessible cookie banners specifically.

What about the other 30%?

The ten above are catchable by automated tools. Roughly 30% of WCAG failures cannot be reliably automated:

  • Whether alt="Photo of CEO" is a meaningful description (versus alt="Anna Müller, CEO, smiling at camera")
  • Whether headings logically describe their sections
  • Whether the reading order makes sense
  • Whether content is understandable in plain language
  • Whether forms recover gracefully from errors

These require either (1) a manual audit by an accessibility specialist or (2) at minimum, a test pass with a real screen reader (NVDA on Windows is free and the default for most blind users in Europe).

For most SMBs, the right move is: automated catches the bulk, then one half-day with a screen reader to verify the load-bearing flows (homepage → product → checkout → account).

On overlay widgets, again, with feeling

If your project manager or boss says "let's just install AccessiBe", here is the conversation:

  • They do not fix the underlying HTML. Lawsuits and complaints proceed on the basis of the source code.
  • They frequently break screen readers that are already configured on the user's device, because they layer a competing interaction model on top.
  • They are not endorsed by EU regulators. France's enforcement actions specifically targeted source-level compliance.
  • The dominant overlay vendor settled with the FTC in 2024 for misleading claims about "WCAG-compliance through one line of JavaScript."

There is no overlay shortcut that survives a complaint. Fix the source.

For a related deep-dive on the legal layers, see our WCAG vs EN 301 549 vs EAA breakdown. For the country-by-country fines that drive these complaints, see our EAA fines guide.

How many of these does your site have right now?

Webply runs a free WCAG 2.1 AA scan against your real templates, ranks issues by EAA legal risk, and gives you the developer-ready fix list. No credit card. No overlay widget.

Sources and further reading

Code examples are illustrative and minimal — production code should be tested with real assistive technology in your specific framework. Not legal advice.

WCAG 2.1 AA
Best Practices
DIY Fixes