Back to Blog
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.
Before we get into the list of violations: the EAA does not cover every website. It targets specific consumer-facing services — online shops, banking, transport ticketing, e-books, telecom, and similar. If your site has a checkout, booking, paid membership, or account flow, you're almost certainly in scope. If you're a pure B2B platform or brochure site, you're probably not. The fines below apply to operators of covered services.
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 (versusalt="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
- WebAIM Million 2024 Annual Accessibility Analysis — largest annual survey of web accessibility on the top 1 million homepages.
- Deque University axe-core 4.10 rules reference — documentation for every axe rule mentioned in this post.
- W3C — WCAG 2.1 Quick Reference and ARIA Authoring Practices.
- European Commission — Directive (EU) 2019/882 (EAA).
- France DGCCRF enforcement actions, November 2025 — Auchan, Carrefour, E.Leclerc, Picard; covered in our EAA fines analysis.
Code examples are illustrative and minimal — production code should be tested with real assistive technology in your specific framework. Not legal advice.
Frequently asked questions
How many of these issues will an automated scan catch?
All ten can be detected by automated tools like axe-core, Lighthouse, or our scanner. They are also the categories that show up most often in enforcement actions because they are easy to verify. Industry research (Deque, WebAIM) consistently finds that automated scanning catches roughly 30–40% of all WCAG failures — the rest (whether alt text is meaningful, whether reading order makes sense) needs manual review.
Are accessibility overlay widgets a valid alternative to fixing the source?
No. Overlay widgets like AccessiBe, UserWay, and EqualWeb do not fix WCAG violations on the underlying HTML — they layer a JavaScript widget on top. Multiple US lawsuits have proceeded against sites WITH overlays installed (most famously the 2021 Eyebobs case and ongoing actions tracked by accessibility advocates). EU regulators have not endorsed overlays, and France specifically requires the source to comply, not a wrapper. They also frequently break screen readers in new ways. We do not recommend them.
How long does it take to fix the top 10?
For a typical SMB website (5–30 templates), a competent developer can resolve all ten categories in 1–3 working days. The bulk of the time is testing — actually opening a screen reader, tabbing through every flow, checking contrast across themes — not writing code.
Which violations are the highest legal risk?
For e-commerce checkouts: missing form labels (#3) and keyboard inaccessibility (#8) are the most likely to trigger a complaint. For content sites: contrast (#2) and missing alt text (#1) are the most cited. For B2B SaaS: empty buttons (#5) and modal focus traps (#9) — because they block account flows entirely.
Can I just rely on my CMS or framework defaults?
Partially. Modern frameworks (Next.js, Nuxt, SvelteKit) ship sensible defaults for the easy stuff — language attributes, semantic HTML. But CMS themes, custom components, and third-party widgets (chat, analytics, cookie banners) routinely break accessibility on top of clean defaults. The framework is a baseline, not a guarantee.
Should I aim for 100% on these fixes?
For 1–6 (the easy structural ones), yes. For 7–10, prioritise by user impact. A modal without a focus trap on a checkout page is critical; the same modal on a "subscribe to newsletter" widget is lower priority. Triaging by user flow is more useful than triaging by severity score alone.