2026-06-01·5 min read·sota.io Team

EAA WCAG 2.1 AA Implementation: Technical Requirements for EU Web Services

Post #2 in the sota.io EU-EAA-DEVELOPER-2026 Series

EAA WCAG 2.1 AA Technical Implementation Guide for EU Web Services

The European Accessibility Act (EAA) — Directive (EU) 2019/882 — does not specify accessibility requirements directly. Instead, it mandates conformance with EN 301 549 v3.2.1, the harmonised European standard that incorporates WCAG 2.1 Level AA as its core technical reference for web-based services.

This post is the practical developer companion to our EAA overview. Where that guide covered scope and enforcement, this guide is code-level: which success criteria apply, what they actually mean for SaaS UIs, and how to build a conformant accessibility stack.

EN 301 549 and WCAG 2.1: The Technical Chain

Understanding the relationship between standards is essential before you write a single line of accessible code:

EAA Directive 2019/882
    ↓ references harmonised standard
EN 301 549 v3.2.1 (2021)
    ↓ Chapter 9 "Web" incorporates
WCAG 2.1 (W3C Recommendation, June 2018)
    ↓ at conformance level
Level AA (50 success criteria)

EN 301 549 is the European standard for ICT accessibility. Chapter 9 covers web content and applications, and it directly adopts WCAG 2.1 Level AA as the required technical baseline. Chapters 11 (Software), 12 (Documentation), and 13 (ICT providing relay or emergency service access) add further requirements, but for most SaaS web applications, WCAG 2.1 AA through Chapter 9 is the primary obligation.

Practical implication: If you build WCAG 2.1 AA conformance into your product, you're meeting the technical core of the EAA. The additional EN 301 549 chapters add non-web-content requirements (authoring tools, documentation, customer support) that many SaaS products also need to address.

The POUR Framework: WCAG's Four Principles

WCAG 2.1 organises all success criteria under four principles. Every SaaS team should understand these before diving into individual criteria:

Principle 1: Perceivable

Users must be able to perceive all information and UI components. Content cannot be invisible to all their senses.

Core requirements:

Principle 2: Operable

Users must be able to operate all interface components and navigation.

Core requirements:

Principle 3: Understandable

Users must be able to understand both the information and the UI operation.

Core requirements:

Principle 4: Robust

Content must be robust enough to be reliably interpreted by assistive technologies.

Core requirements:

The 50 Level AA Success Criteria by Category

This is the exhaustive list of what you must implement. Level A (must) + Level AA (should, but mandated by EAA) = 50 criteria total.

Perceivable — Level A (must implement)

1.1.1 Non-text Content (A): Every image, icon, button, chart, and decorative element needs a text alternative. Icons that convey meaning need aria-label. Decorative images need alt="". Charts need descriptions.

<!-- Image with meaning -->
<img src="error-icon.svg" alt="Error: form submission failed" />

<!-- Decorative image -->
<img src="background-texture.png" alt="" role="presentation" />

<!-- Icon button -->
<button aria-label="Delete item">
  <svg aria-hidden="true"><!-- icon SVG --></svg>
</button>

1.2.1 Audio-only and Video-only (A): If you have product demo videos or audio announcements, provide alternatives. Pre-recorded audio needs a transcript. Pre-recorded video-only needs audio description or text alternative.

1.2.2 Captions (Pre-recorded) (A): All pre-recorded video with audio must have accurate captions. YouTube's auto-captions do not meet this requirement — they must be reviewed and corrected.

1.2.3 Audio Description or Media Alternative (A): Pre-recorded video must either have an audio description track or a full text alternative that presents equivalent information.

1.3.1 Info and Relationships (A): Structure conveyed visually must also be conveyed programmatically. The most commonly failed criterion in SaaS UIs:

<!-- WRONG: visual table that isn't semantic -->
<div class="table">
  <div class="row header"><div>Name</div><div>Status</div></div>
  <div class="row"><div>Project A</div><div>Active</div></div>
</div>

<!-- RIGHT: semantic table -->
<table>
  <thead>
    <tr><th scope="col">Name</th><th scope="col">Status</th></tr>
  </thead>
  <tbody>
    <tr><td>Project A</td><td>Active</td></tr>
  </tbody>
</table>

Form labels, heading hierarchy, list structure — all must be semantically correct.

1.3.2 Meaningful Sequence (A): Reading order must make sense when CSS is removed. Modal dialogs that appear at the end of the DOM but visually overlay the page are a common failure.

1.3.3 Sensory Characteristics (A): Instructions cannot rely solely on shape, colour, size, visual location, orientation, or sound. "Click the red button" fails. "Click the Submit button" passes.

1.4.1 Use of Colour (A): Colour cannot be the only visual means of conveying information. Status indicators that use only red/green need a secondary indicator (icon, text, pattern).

<!-- WRONG: colour only -->
<span class="status-red">Failed</span>

<!-- RIGHT: colour + text/icon -->
<span class="status-red" aria-label="Status: Failed">
  <svg aria-hidden="true"><!-- x-circle icon --></svg>
  Failed
</span>

1.4.2 Audio Control (A): If audio plays automatically for more than 3 seconds, provide a mechanism to pause, stop, or control volume independent of the system volume.

Perceivable — Level AA (EAA mandated)

1.2.4 Captions (Live) (AA): Live video streams (webinars, product demos) must have live captions. This is complex to implement but required for in-scope live broadcasts.

1.2.5 Audio Description (Pre-recorded) (AA): All pre-recorded video must have audio descriptions for visual content that is not audible (actions, text on screen, scene descriptions).

1.3.4 Orientation (AA): Content must not restrict its view and operation to a single display orientation, unless a specific orientation is essential. Lock your UI to portrait/landscape only if it's functionally necessary.

1.3.5 Identify Input Purpose (AA): Input fields collecting user data must identify their purpose via autocomplete attributes. This helps users with cognitive disabilities and autofill:

<input type="email" name="email" autocomplete="email" />
<input type="text" name="name" autocomplete="name" />
<input type="tel" name="phone" autocomplete="tel" />

1.4.3 Contrast (Minimum) (AA): Normal text (below 18pt / 14pt bold) needs a 4.5:1 contrast ratio against background. Large text needs 3:1. This fails frequently in dark-mode SaaS UIs with low-contrast secondary text.

Body text #6b7280 on #ffffff = 4.05:1 — FAIL
Body text #6b7280 on #f9fafb = 3.89:1 — FAIL
Body text #374151 on #ffffff = 8.59:1 — PASS

1.4.4 Resize Text (AA): Text must be resizable up to 200% without assistive technology and without loss of content or functionality. Avoid px-fixed font sizes; use rem or em.

1.4.5 Images of Text (AA): Avoid using images to display text that could be rendered as actual text. Logos are exempt; "Join 10,000+ developers" banners rendered as PNG are not.

1.4.10 Reflow (AA): Content must be presentable without horizontal scrolling at a viewport width equivalent to 320 CSS pixels (400% zoom on a 1280px display). This is the single most commonly failed criterion in enterprise SaaS.

/* Horizontal scroll is the failure mode */
/* Avoid fixed widths; use max-width and flexible layouts */
.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

1.4.11 Non-text Contrast (AA): UI components (input borders, focus indicators, chart series) and informational graphics need a 3:1 contrast ratio. Thin border-color: #d1d5db on white backgrounds commonly fails.

1.4.12 Text Spacing (AA): Users must be able to apply custom text spacing (line height 1.5×, letter spacing 0.12em, word spacing 0.16em, paragraph spacing 2em) without loss of content or functionality. Test with the Text Spacing Bookmarklet.

1.4.13 Content on Hover or Focus (AA): Custom tooltips and hover content that appears must be: dismissible (Escape key), hoverable (pointer can move to the tooltip), and persistent (content does not disappear if pointer moves off trigger).

Operable — Level A

2.1.1 Keyboard (A): All functionality available by mouse must be available by keyboard. Every interactive element must be reachable and operable via Tab/Shift-Tab/Enter/Space/arrow keys. Custom widgets (date pickers, drag-and-drop, rich text editors) are the common failure points.

2.1.2 No Keyboard Trap (A): Keyboard focus must never be trapped in a component where there is no escape (except modals, which must trap focus intentionally and provide an explicit dismiss mechanism). Inline iframes with no keyboard escape are a frequent violation.

2.1.4 Character Key Shortcuts (A): Single-key shortcuts activated without modifier keys must be remappable, disableable, or only active when a component has focus.

2.2.1 Timing Adjustable (A): If time limits exist (session timeouts, auto-submitting forms), users must be able to turn them off, adjust them, or get at least 20 seconds to extend them.

2.2.2 Pause, Stop, Hide (A): Moving, blinking, scrolling content that starts automatically and lasts more than 5 seconds must be pausable or stoppable.

2.3.1 Three Flashes or Below Threshold (A): No content that flashes more than three times per second. This is a hard safety requirement, not just an accessibility preference.

2.4.1 Bypass Blocks (A): Provide a mechanism to skip repeated blocks of navigation (skip-to-main-content link). This must be the first focusable element in the page.

<a class="skip-link" href="#main-content">Skip to main content</a>
<nav><!-- navigation --></nav>
<main id="main-content"><!-- content --></main>

2.4.2 Page Titled (A): Each page must have a descriptive <title> that identifies the page and site. "Dashboard | sota.io" is better than "Dashboard". "Untitled" fails.

2.4.3 Focus Order (A): Navigation order must be logical and meaningful — top to bottom, left to right in LTR layouts. DOM order determines focus order; visual CSS reordering (order, flex-direction: row-reverse) can break this.

2.4.4 Link Purpose (In Context) (A): The purpose of every link must be determinable from link text or link context. "Click here" and "Read more" fail unless the surrounding context makes the destination clear.

2.5.1 Pointer Gestures (A): All functionality using multipoint or path-based gestures must be operable with a single pointer. Pinch-to-zoom, swipe gestures, and draw-to-interact must have single-click/tap alternatives.

2.5.2 Pointer Cancellation (A): For single-pointer activation, at least one of: no down-event activation, abort/undo, up-event activation, or essential exception. This prevents accidental activations.

2.5.3 Label in Name (A): Accessible name (aria-label) must contain the visible text label. If a button says "Submit", aria-label="Submit form data" is fine. aria-label="Go" when the button says "Submit" fails.

2.5.4 Motion Actuation (A): Functionality triggered by device motion (shake to undo, tilt to scroll) must have UI alternatives and the motion trigger must be disableable.

Operable — Level AA

2.4.5 Multiple Ways (AA): More than one way must exist to locate a page within a set of pages — navigation menu + site search + breadcrumbs, or sitemap. An app with only a linear wizard flow and no navigation fails this.

2.4.6 Headings and Labels (AA): Headings and labels must describe the topic or purpose. "Section 3" fails. "User Permissions Configuration" passes.

2.4.7 Focus Visible (AA): Any keyboard-operable UI must have a visible keyboard focus indicator. Removing :focus outlines with outline: none without a replacement is one of the most common accessibility bugs in SaaS design systems.

/* WRONG: removes all focus indicators */
* { outline: none; }

/* RIGHT: visible custom focus style */
:focus-visible {
  outline: 2px solid #10b981;
  outline-offset: 2px;
}

2.5.6 Concurrent Input Mechanisms (AA): Content must not restrict input mechanisms available to the platform. Don't lock users to touch-only or mouse-only interaction modes.

Understandable — Level A

3.1.1 Language of Page (A): The default language of the page must be declared in <html lang="en">. Missing or wrong lang attribute causes screen readers to pronounce text incorrectly.

3.2.1 On Focus (A): Receiving focus must not cause a context change (navigation, form submission, popup). Removing focus from an input must not submit a form.

3.2.2 On Input (A): Changing the value of a UI component must not automatically change context unless the user was advised in advance.

3.3.1 Error Identification (A): Errors must be identified in text and described to the user. Red border alone is insufficient — the field must have error text associated via aria-describedby.

<div>
  <label for="email">Email address</label>
  <input id="email" type="email" aria-describedby="email-error" aria-invalid="true" />
  <span id="email-error" role="alert">Please enter a valid email address.</span>
</div>

3.3.2 Labels or Instructions (A): When user input is required, labels or instructions must be provided. Placeholder text alone is not a label — it disappears on input and has insufficient contrast.

Understandable — Level AA

3.1.2 Language of Parts (AA): Passages in a different language must be identified with lang attribute. If your SaaS has multilingual content blocks, annotate them.

3.2.3 Consistent Navigation (AA): Navigation mechanisms repeated across pages must occur in the same relative order — don't rearrange the sidebar or header navigation between pages.

3.2.4 Consistent Identification (AA): Components with the same functionality across pages must be consistently identified. The logout button's accessible name must be consistent everywhere it appears.

3.3.3 Error Suggestion (AA): When an error is detected, provide suggestions for correction where possible (without compromising security). "Password must be 8+ characters" is better than "Invalid password".

3.3.4 Error Prevention (Legal, Financial, Data) (AA): For submissions with legal consequences, financial transactions, or data modification that cannot be recovered: provide at least one of — reversibility (undo), checking (review before submit), or confirmation (explicit confirm step).

Robust — Level A

4.1.1 Parsing (A): HTML must be parseable — unique IDs, properly nested elements, complete start/end tags. Run your HTML through the W3C Validator.

4.1.2 Name, Role, Value (A): All UI components must expose their name, role, and value to assistive technologies. Custom components built from div and span must use ARIA roles:

<!-- WRONG: div masquerading as a button -->
<div onclick="submit()" class="btn-primary">Submit</div>

<!-- RIGHT: semantic HTML -->
<button type="submit">Submit</button>

<!-- RIGHT: ARIA if semantic HTML is not possible -->
<div role="button" tabindex="0" onclick="submit()" 
     onkeydown="handleKey(event)" class="btn-primary">Submit</div>

Robust — Level AA

4.1.3 Status Messages (AA): Status messages (form submission confirmation, error count, progress updates) must be programmatically determinable without receiving focus, using role="alert", role="status", or aria-live regions.

<!-- Success notification that doesn't steal focus -->
<div role="status" aria-live="polite" aria-atomic="true">
  Project saved successfully.
</div>

<!-- Error alert that interrupts (use sparingly) -->
<div role="alert" aria-live="assertive" aria-atomic="true">
  3 validation errors. Please correct them before continuing.
</div>

Common SaaS Failure Patterns (Ranked by Frequency)

Based on accessibility audits of typical SaaS applications, these are the most common violations:

RankCriterionCommon Cause
11.4.3 ContrastLow-contrast secondary text, disabled state colours
24.1.2 Name/Role/ValueCustom components without ARIA, icon buttons without labels
31.3.1 Info and RelationshipsVisual-only tables, non-semantic form structure
42.4.7 Focus Visibleoutline: none in design system reset
51.4.10 ReflowFixed-width sidebars, non-responsive data tables
62.1.1 KeyboardDrag-and-drop without keyboard alternative
74.1.1 ParsingDuplicate IDs, broken ARIA references
81.3.5 Identify Input PurposeMissing autocomplete attributes
93.3.1 Error IdentificationColour-only error state
104.1.3 Status MessagesToast notifications without aria-live

The Minimum Viable Accessibility Stack

For a SaaS team starting from scratch, implement in this priority order:

Phase 1: Structural Fixes (Week 1–2)

These eliminate the majority of screen reader failures:

  1. Semantic HTML audit: Replace div/span buttons with button, ensure heading hierarchy (h1→h2→h3), use nav, main, header, footer landmark elements
  2. Skip link: Add <a class="skip-link" href="#main">Skip to main content</a> as first focusable element
  3. Form labels: Audit every form input for programmatic label association (for/id pairing or aria-labelledby)
  4. Image alt text: Audit all images — meaningful alt text for informative images, empty alt for decorative
  5. Page titles: Ensure unique, descriptive <title> on every page/route

Phase 2: Visual Fixes (Week 3–4)

  1. Contrast audit: Run all text and UI component colours through a contrast checker — target 4.5:1 for body text, 3:1 for large text and UI components
  2. Focus indicators: Add visible :focus-visible styles to your design system — at minimum outline: 2px solid <brand-colour>
  3. Colour-only indicators: Add secondary indicators (icons, text) to all status-only colour coding

Phase 3: Interactive Fixes (Week 5–8)

  1. Keyboard navigation audit: Tab through your entire app with mouse disabled — every interaction must be reachable
  2. ARIA on custom components: Date pickers, modals, dropdowns, data grids — all need proper ARIA roles, states, and properties
  3. aria-live regions: Add status announcements for async operations, form submissions, real-time updates
  4. Error handling: Implement programmatic error association with aria-describedby and aria-invalid

Phase 4: Remaining Criteria (Ongoing)

Orientation, language declarations, autocomplete attributes, motion reduction (prefers-reduced-motion), and the detailed interaction patterns.

The Accessibility Statement: What the EAA Requires

Under Article 13(2) of Directive 2019/882 (as transposed in national law), service providers must publish an accessibility statement. The statement must include:

  1. The services covered
  2. The applicable standard (EN 301 549 v3.2.1 / WCAG 2.1 AA)
  3. Conformance status: Fully conformant / Partially conformant / Non-conformant
  4. Known non-conformances with reasons and alternatives
  5. A feedback mechanism (email or form where users can report barriers)
  6. An enforcement contact (relevant National Competent Authority)
  7. The date of the statement and last review date

Template minimum viable statement:

Accessibility Statement for [Service Name]

[Company] is committed to ensuring digital accessibility in line with 
Directive (EU) 2019/882 (European Accessibility Act) and EN 301 549 v3.2.1.

Conformance status: Partially conformant. [Service] partially conforms 
to WCAG 2.1 Level AA. The following content does not fully conform:

- [List known issues]

Feedback: To report accessibility issues, email accessibility@[domain] 
or use our feedback form at [URL].

Enforcement: If you are unsatisfied with our response, contact [NCA name 
and URL].

This statement was last reviewed on [date].

The statement must be publicly accessible — link it from your product footer and <head> (<link rel="accessibility" href="/accessibility">).

Testing Methodology

A conformant EAA submission requires both automated and manual testing:

Automated Tools (Catch ~30–40% of issues)

ToolTypeBest For
axe-coreLibraryCI integration
axe DevToolsBrowser extensionDevelopment
Lighthouse AccessibilityBrowser/CIScore tracking
WAVEBrowser extensionVisual review
Pa11yCLIBatch URL testing
// package.json — add to test suite
{
  "scripts": {
    "test:a11y": "pa11y-ci --sitemap https://yourapp.com/sitemap.xml"
  }
}

Manual Testing (Catch the other 60%)

  1. Keyboard-only navigation: Unplug mouse, navigate your app using only Tab, Shift-Tab, Enter, Space, and arrow keys
  2. Screen reader testing:
    • Windows: NVDA (free) + Chrome, JAWS + Chrome
    • macOS: VoiceOver (built-in) + Safari
    • iOS: VoiceOver + Safari
    • Android: TalkBack + Chrome
  3. 200% zoom test: Browser zoom to 200%, confirm no content is cut off
  4. 400% zoom / reflow test: Set viewport to 320px equivalent, verify no horizontal scroll for content
  5. Colour contrast verification: Use browser dev tools' accessibility inspector or Colour Contrast Analyser
  6. Text spacing test: Apply the Text Spacing Bookmarklet

CI/CD Integration

# GitHub Actions — accessibility check on PR
name: Accessibility Tests
on: [pull_request]
jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install
      - run: npm run build
      - run: npx pa11y-ci ./test-urls.txt

React/Vue/Angular: Framework-Specific Guidance

React

if (process.env.NODE_ENV === 'development') {
  const axe = await import('@axe-core/react')
  axe.default(React, ReactDOM, 1000)
}

Vue

Angular

What Qualifies as Disproportionate Burden

Article 14 of Directive 2019/882 allows service providers to invoke disproportionate burden as a justification for not implementing specific requirements. This is not a blanket exemption — it must be assessed per requirement, documented, and reviewed every five years.

The assessment must weigh:

Important: This exemption does not apply to fundamental accessibility obligations (basic structural markup, keyboard navigation, alt text for primary content). It is intended for edge cases like legacy embedded third-party content or highly specialised technical tools.

Enforcement Timeline and Risk Assessment

The EAA has been enforceable since June 28, 2025. Current NCA enforcement posture by sector:

CountryNCAEnforcement Status
GermanyBMAS / BundesfachstelleActive — BFSG transposition, formal complaints accepted
FranceDINUMActive — RGAA references updated to EN 301 549
NetherlandsAccessibility FoundationActive — enforcement via complaints
IrelandDCEDIYActive — monitoring phase
SwedenDIGGActive — audit programme running

Complaint-driven enforcement is the current pattern — NCAs respond to complaints from users, disability organisations, and advocacy groups. Proactive audits are increasing in 2026 as the one-year post-application deadline passed.

Risk profile for non-compliant SaaS:

Next in This Series

In the next post, we'll cover EAA Mobile App Accessibility — iOS, Android, and Progressive Web App requirements under Directive 2019/882, including WCAG 2.1 AA application to native mobile components and the specific PWA considerations that many teams overlook.


Hosting a SaaS with strict EU data residency requirements? sota.io is EU-native managed PaaS — deploy from git, 100% GDPR, Hetzner Germany, no CLOUD Act exposure.

EU-Native Hosting

Ready to move to EU-sovereign infrastructure?

sota.io is a German-hosted PaaS — no CLOUD Act exposure, no US jurisdiction, full GDPR compliance by design. Deploy your first app in minutes.