Web accessibility is no longer just a best practice, it has now become a legal requirement in the European Union. With the European Accessibility Act (EAA) setting clear expectations for digital accessibility, developers must ensure their applications are inclusive for all users.
In this post, we’ll break down what EAA compliance means for developers, the legal risks of non-compliance, common accessibility (often abbreviated to A11y) mistakes, and practical steps to build better, more inclusive applications. We will review some A11y friendly libraries for modern frameworks and in the end you will get a repository that contains accessible web development practices in action.
Who needs to comply with the EAA?
The European Accessibility Act (Directive (EU) 2019/882) applies to companies that provide digital products and services in the EU. The deadline to comply is June 2025. The law covers:
- E-commerce platforms: Online stores and digital marketplaces
- Banking and financial services: Mobile banking apps, payment gateways
- Streaming and media services: Video-on-demand platforms, digital libraries
- Public sector and essential services: Government portals, healthcare websites
- Software and SaaS products: Enterprise applications, productivity tools
Are there exemptions?
Micro-businesses (fewer than 10 employees and less than €2 million annual turnover) may be exempt, however accessibility improves user experience and market reach for all companies.
Legal consequences of non-compliance
Failing to meet EAA accessibility requirements comes with the following risks:
- Fines and sanctions: Each EU country can enforce penalties, which can be costly.
- Legal challenges: Consumers and advocacy groups can take legal action.
- Market restrictions: Non-compliant services may be blocked from operating in the EU.
Beyond the legal aspect, inaccessible websites exclude millions of users and hurt usability for everyone. Again, addressing accessibility early on, prevents compliance issues and improves user experience for everyone.
Common accessibility mistakes (and how to fix them)
We developers, often make accessibility mistakes without realizing it. Below, we will outline common use cases and how to make them compliant and more accessible, categorized into must-have fixes for compliance and nice-to-have improvements for better usability.
Must-Haves: Essentials for accessibility compliance
1. Ensuring the page head is accessible
The <head>
section of an HTML document is often overlooked, but it plays a crucial role in accessibility.
Must-have elements in the <head>
section:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="An example of an accessible webpage compliant with the European Accessibility Act.">
<title>Accessible Webpage Example</title>
<link rel="stylesheet" href="styles.css">
</head>
-
lang="en"
on<html>
– Specifies the primary language for screen readers. - Descriptive
<title>
– Helps users (especially those using screen readers) understand the page's purpose. -
meta name="description"
– Provides context when the page appears in search results. -
meta name="viewport"
– Ensures proper scaling and readability on mobile devices.
2. Using aria-labelledby for section headings
Screen readers should clearly associate form groups and section headings with their related content.
Correct usage:
<section id="contact" aria-labelledby="contact-heading">
<h2 id="contact-heading">Contact Us</h2>
<form>
...
</form>
</section>
-
aria-labelledby="contact-heading"
– Ensures screen readers associate the heading with the section.
3. Adding proper labels and ARIA attributes to forms
Forms without labels are confusing for screen reader users because they don’t provide context for input fields. Additionally, users need real-time feedback when errors occur.
Example:
<input type="text" placeholder="Enter your name">
Accessible implementation:
<label for="name">Name:</label>
<input
type="text"
id="name"
name="name"
placeholder="Enter your name"
required
aria-invalid="false"
aria-describedby="name-error">
<span id="name-error" class="error-message" aria-live="polite"></span>
-
aria-invalid="false"
– Indicates that the field is valid (should be set to "true" when an error occurs). -
aria-describedby="name-error"
– Links the input field to an error message. -
aria-live="polite"
– Ensures error messages are announced dynamically by screen readers.
Handling validation dynamically with VanillaJS:
const nameInput = document.getElementById("name");
const nameError = document.getElementById("name-error");
nameInput.addEventListener("input", () => {
if (nameInput.value.trim() === "") {
nameInput.setAttribute("aria-invalid", "true");
nameError.textContent = "Name is required.";
} else {
nameInput.setAttribute("aria-invalid", "false");
nameError.textContent = "";
}
});
4. Important state changes announcements
Users should be informed about important state changes on the UI, for instance whether their password is currently visible or hidden.
Common use case:
<input type="password" id="password">
<button onclick="togglePassword()">Show</button>
Accessible implementation:
<label for="password">Password:</label>
<div style="position: relative;">
<input
type="password"
id="password"
name="password"
required
aria-invalid="false"
aria-describedby="password-visibility">
<button
type="button"
id="toggle-password-visibility"
aria-label="Show password">
Show
</button>
</div>
<span id="password-visibility" class="visually-hidden" aria-live="polite">
Password is currently hidden.
</span>
VanillaJS to update the state dynamically:
const passwordField = document.getElementById("password");
const toggleButton = document.getElementById("toggle-password-visibility");
const visibilityMessage = document.getElementById("password-visibility");
toggleButton.addEventListener("click", () => {
const isPasswordVisible = passwordField.type === "text";
passwordField.type = isPasswordVisible ? "password" : "text";
toggleButton.textContent = isPasswordVisible ? "Show" : "Hide";
visibilityMessage.textContent = isPasswordVisible
? "Password is currently hidden."
: "Password is currently visible.";
});
This ensures that screen readers will announce the password state change properly.
5. Providing visible focus indicators
Keyboard users need to see where their focus is, but sometimes we remove default focus styles for aesthetic reasons.
Common mistake:
button:focus {
outline: none;
}
Correct usage:
button:focus {
outline: 3px solid #ffcc00;
}
For a better visual experience, we could as well use :focus-visible
to make focus styles appear only when navigating via keyboard:
button:focus-visible {
outline: 3px solid #ffcc00;
}
6. Missing or incorrect alt text for images
Screen readers rely on alt attributes to describe images to visually impaired users. Without them, images are either ignored or announced as "image," providing no useful context.
Common mistake:
<img src="team.webp">
Correct usage:
<img src="team.webp" alt="A diverse team of eight people smiling in an office">
If an image is decorative and does not add meaningful content, use alt="" so screen readers ignore it.
7. Providing video captions for multimedia content
Video content must include captions to ensure accessibility for users who are deaf or hard of hearing.
Incorrect usage:
<video controls>
<source src="intro.mp4" type="video/mp4">
</video>
Correct usage:
<video controls>
<source src="intro.mp4" type="video/mp4">
<track src="captions.vtt" kind="subtitles" srclang="en" label="English">
</video>
You can find an example of a captions file here.
8. Ensuring keyboard navigation support
Native interactive elements like <button>
, <a href="">
, <input>
, <select>
, and <textarea>
are automatically focusable and included in the natural tab order. So we would not need to add tabindex to a <button>
, since it is already keyboard-focusable.
However, in case we would need to make non-interactive elements like <div>
, <span>
, or <p>
keyboard-focusable, we would need to use tabindex=n.
Example:
<span
role="button">
I am a custom button!
</span>
Accessible implementation:
<span
role="button"
tabindex="0">
I am a custom button!
</span>
Here, a static HTML element (<span>
) is used to mark up the control, and is exposed as such to assistive technologies through the application of role="button"
. However, because it is marked up using a <span>
element, it won't be focusable without tabindex="0"
.
Nice-to-haves: Further enhancements
1. Announcing state changes with ARIA live sections
For example, users should receive feedback when a form submission succeeds or fails.
Use case:
<p id="form-status">Form submitted successfully!</p>
Accessible implementation:
<!-- idle state -->
<p id="form-status" role="status" aria-live="polite"></p>
<!-- when form has been submitted -->
<p id="form-status" role="status" aria-live="polite">
Form submitted successfully!
</p>
-
aria-live="polite"
– Ensures the message is announced by screen readers. Whenaria-live
's attribute is set to polite, assistive technologies will notify users of updates but generally do not interrupt the current task, with the updates having a low priority. When set toassertive
, assistive technologies immediately notify the user, potentially clearing the speech queue of previous updates. -
role="status"
– Indicates that this element provides status updates.
2. Reducing motion for users with vestibular disorders
Animations can cause discomfort for users sensitive to motion.
Correct approach:
@media (prefers-reduced-motion: reduce) {
* {
animation: none;
transition: none;
}
}
This respects the user’s system settings for reduced motion.
Accessibility-friendly libraries and tools in modern frameworks
Angular
For Angular applications, consider using:
- @angular/cdk/a11y – In latest Angular versions the Component Development Kit (CDK) includes the a11y package. We can find some useful examples and usages at the official documentation.
- Angular Material — All Material components follow accessibility best practices.
React
For React applications, these libraries can prove really helpful:
- @react-aria (Adobe’s React Aria) – Provides UI primitives that enable us to build accessible components.
- React Helmet — Helps manage document structure and metadata for screen readers e.g. provide a unique title for each page or view.
Testing & Validating Accessibility
To ensure and maintain accessibility, our websites should be tested using a combination of automated tools and real user feedback.
1. Automated Testing
- Lighthouse (Chrome DevTools): With the accessibility report we can run audits to detect WCAG violations.
- axe DevTools (Browser Extension): Provides detailed accessibility reports.
2. Screen Reader Testing
- VoiceOver (Mac) — Press CMD + F5 to activate.
- NVDA (Windows) — Free and widely used screen reader.
3. Keyboard Navigation Testing
- Use
TAB
andSHIFT + TAB
to navigate through your websites and your forms. - Ensure all interactive elements are accessible and state changes are being announced.
4. User Testing
- Test with different accessibility settings (high contrast mode, reduced motion).
- Engage users with disabilities for real-world feedback.
Final Thoughts: Accessibility as a Continuous Effort
Accessibility isn’t a single task, it should be an ongoing commitment to creating better digital experiences for everyone.
- Start with must-have fixes to meet compliance requirements.
- Implement nice-to-have improvements to enhance usability.
- Continuously test and iterate to maintain accessibility standards.
- By prioritizing accessibility, we can build applications that are not only legally compliant but also more inclusive and user-friendly.
In case you are looking for more real-world examples, I’ve recently put together a repository that contains accessible web development practices in action.
dimeloper
/
eaa-compliance-examples
This repository contains examples of accessible web development practices to help developers comply with the European Accessibility Act (EAA).
EAA Compliance Examples
This repository contains examples of accessible web development practices to help developers comply with the European Accessibility Act (EAA). The examples demonstrate techniques for creating inclusive, user-friendly webpages for all users.
Features
- Keyboard Accessibility: Includes skip links and focus indicators.
- Accessible Forms: Dynamically validates inputs with error messages announced to assistive technologies.
- Password Field Enhancements: Visibility toggle with live announcements and error handling.
- Multimedia Accessibility: Provides captions for videos and alt text for images.
- Semantic HTML: Uses appropriate elements for structure and navigation.
- Live Regions: Dynamic updates for screen readers using ARIA.
File Structure
eaa-compliance-examples/
├── index.html # Main HTML document
├── styles.css # External CSS for styling
├── script.js # JavaScript for form interactions
├── captions/
│ └── captions.vtt # Example captions file for the video
├── intro.mp4 # Introductory video showcasing inclusivity
├── README.md # Documentation for the
…
Top comments (0)