I spent the last three months auditing 14 production websites for European Accessibility Act compliance. Some were e-commerce stores, a few were SaaS dashboards, and a couple were content-heavy marketing sites. The codebases ranged from hand-rolled HTML to Next.js to WordPress with a dozen plugins stacked on top.
Here's what surprised me: the same five violations showed up on nearly every single site. Not obscure edge cases. Basic stuff. The kind of problems that affect real users every day and that take 15 minutes to fix once you know what to look for.
1. Missing or meaningless alt text on images
This one topped the list on 13 out of 14 sites. And it wasn't just missing alt attributes -- it was alt text that said things like "image1.png" or "Screenshot 2024-03-15" or just "photo."
Screen readers announce these verbatim. Imagine navigating a product page and hearing "image slash products slash DSC underscore 4782 dot jpeg" repeated six times. That's the experience you're delivering.
The fix depends on context. If the image conveys information, describe what matters:
<!-- Bad -->
<img src="/products/blue-widget.jpg" alt="photo">
<!-- Good -->
<img src="/products/blue-widget.jpg" alt="Blue ceramic widget, 12cm tall, with matte finish">
If it's purely decorative -- a background flourish, a divider graphic -- mark it as such:
<img src="/divider.svg" alt="" role="presentation">
The empty alt attribute tells assistive technology to skip it entirely. That's not laziness; it's the correct semantic choice when an image adds nothing to the content.
One thing I kept seeing in React codebases: developers passing alt as a prop but never actually requiring it. Add an ESLint rule. jsx-a11y/alt-text catches this at build time. Don't rely on code review for something a linter can enforce.
2. Form inputs without associated labels
This was the violation that made me the most frustrated, because it's so easy to get right and so common to get wrong. On 11 of 14 sites, I found at least one form where inputs had placeholder text but no <label> element.
Placeholders are not labels. They disappear when you start typing, which is a usability problem for everyone, but for screen reader users the situation is worse: many assistive technologies don't reliably announce placeholder text as the input's accessible name.
<!-- This looks fine visually but is broken for AT users -->
<input type="email" placeholder="Enter your email">
<!-- This works -->
<label for="email-input">Email address</label>
<input type="email" id="email-input" placeholder="e.g. jane@example.com">
If your design really can't accommodate a visible label (and I'd push back on that), use a visually hidden label. The .sr-only pattern has been around for years:
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
<label for="search" class="sr-only">Search products</label>
<input type="search" id="search" placeholder="Search...">
I'll be honest: the visually hidden approach is a compromise. A visible label is always better. But it's infinitely better than no label at all.
3. Insufficient color contrast
Every single site I audited had at least one contrast failure. Every one. It's the most widespread accessibility issue on the web and has been for years.
The WCAG requirement is a 4.5:1 ratio for normal text and 3:1 for large text (18px bold or 24px regular). Light gray text on white backgrounds was the biggest offender. I found one site using #999 on #fff for body text -- that's a 2.8:1 ratio.
/* Fails - 2.8:1 contrast ratio */
.muted-text {
color: #999;
background: #fff;
}
/* Passes - 4.6:1 contrast ratio */
.muted-text {
color: #767676;
background: #fff;
}
The difference between #999 and #767676 is subtle visually but meaningful for readability, especially for users with low vision. Use a tool like the WebAIM Contrast Checker or the Chrome DevTools contrast picker (it's built right into the color selector now) to verify your ratios as you work.
The tricky part is buttons and interactive elements. I found several sites where the button text passed contrast requirements against the button background, but the button background failed against the page background. Both relationships matter for users who need to identify interactive elements.
4. No keyboard focus indicators
Removing the default focus outline is one of those CSS "best practices" that spread through the frontend community years ago and never fully went away. I still see *:focus { outline: none; } in production stylesheets.
On 9 of 14 sites, I could tab through the interface and had no idea where I was. No visible focus state at all. For keyboard users -- and that includes people using switch devices, people with motor impairments, and power users who prefer the keyboard -- this makes a site completely unusable.
/* Don't do this */
*:focus {
outline: none;
}
/* Do this instead */
*:focus-visible {
outline: 2px solid #1a73e8;
outline-offset: 2px;
}
The :focus-visible pseudo-class is the key here. It only shows the focus indicator when the user is navigating via keyboard, not when they click with a mouse. This solves the design concern that led people to remove outlines in the first place.
If you're working in a design system, make the focus style part of your token system. Define it once, apply it everywhere. I've seen teams fix this issue in one file and ship it site-wide in under an hour.
5. Non-semantic interactive elements
The last pattern I saw repeatedly: <div> and <span> elements with click handlers acting as buttons or links. On 8 of the 14 sites, critical interactions were built on elements that assistive technology doesn't recognize as interactive.
<!-- Broken: AT doesn't know this is clickable -->
<div class="btn-primary" onclick="submitForm()">Submit</div>
<!-- Fixed: semantic HTML does the heavy lifting -->
<button type="submit" class="btn-primary">Submit</button>
When you use a <div> as a button, you lose: keyboard focusability, the button role announcement, Enter/Space key activation, and proper form submission behavior. You can bolt all of that back on with tabindex, role, and keydown handlers, but why? The <button> element gives you all of it for free.
The same goes for links. If clicking something navigates the user somewhere, it should be an <a> with an href. If it triggers an action on the current page, it should be a <button>. This isn't pedantic -- it directly determines how assistive technology communicates the element's purpose.
The pattern I noticed
These five issues share something in common: none of them require specialized accessibility knowledge. They're HTML fundamentals. Use the right elements. Label your inputs. Don't hide critical visual information from users.
The European Accessibility Act enforcement begins in June 2025 for new products and services. If you haven't audited your site yet, start with these five. You'll catch the majority of issues that actually affect users, and most of the fixes are small enough to ship in a single PR.
I put together a free compliance checklist that walks through these violations and more, organized by component type so you can work through your codebase systematically. Grab it at agentkit.kit.com -- it's the same checklist I use on client audits.
Top comments (0)