Accessibility-Testing automatisieren mit axe-core
PASS
expect()
Testing · Accessibility · axe-core · E2E
Accessibility-Testing automatisieren mit axe-core
Automatisierte A11y-Checks in Cypress und Playwright

Automatisierte Accessibility-Tests mit axe-core decken zwar nur rund 30 bis 40 Prozent aller WCAG-Kriterien ab, doch genau diese Kategorien wie Farbkontraste, fehlende Alt-Texte und ARIA-Fehler lassen sich zuverlässig in jede Cypress- und Playwright-Pipeline integrieren. Dieser Artikel zeigt, wie ihr Schwellenwerte konfiguriert, CI-Builds gezielt nur bei neuen Verstößen blockiert und typische Magento- sowie Hyvä-spezifische Barrieren frühzeitig erkennt.

14 Min. Lesezeit axe-core · Cypress · Playwright WCAG 2.1 · CI/CD · Magento 2

1. Warum automatisierte Accessibility-Tests im E2E-Workflow unverzichtbar sind

Barrierefreiheit ist längst keine Kür mehr: Mit dem Barrierefreiheitsstärkungsgesetz (BFSG) und dem European Accessibility Act sind digitale Shops in der EU seit 2025 rechtlich zu barrierefreien Online-Angeboten verpflichtet. Manuelle Audits durch externe Prüfstellen sind teuer und finden meist erst kurz vor einem Release statt, wenn Korrekturen bereits aufwendig sind. Wer Accessibility-Checks stattdessen direkt in die bestehende E2E-Test-Suite integriert, erkennt Regressionen genauso früh wie funktionale Bugs, nämlich bei jedem Pull Request.

axe-core ist die von Deque Systems entwickelte Open-Source-Engine, die auch vielen anderen Tools wie Lighthouse und WAVE als Grundlage dient. Als npm-Bibliothek lässt sie sich direkt in Cypress oder Playwright einbinden und prüft den tatsächlich gerenderten DOM zur Laufzeit, nicht nur statisches HTML. Das ist entscheidend für moderne Frontends: Ein Alpine.js-Dropdown oder ein dynamisch nachgeladenes Formular existiert im Quellcode oft anders als im finalen DOM, und nur die Laufzeitprüfung erfasst den tatsächlichen Nutzerzustand.

2. Was axe-core automatisch erkennt und wo manuelle Prüfung nötig bleibt

Nach Untersuchungen von Deque lassen sich realistisch etwa 30 bis 40 Prozent aller WCAG-Erfolgskriterien zuverlässig automatisiert prüfen. Dazu zählen unzureichender Farbkontrast (WCAG 1.4.3), fehlende alt-Attribute an Bildern (1.1.1), Formularfelder ohne zugeordnetes Label (1.3.1 und 4.1.2), fehlerhafte oder doppelte ARIA-Rollen und -Attribute, eine unlogische Heading-Hierarchie sowie ein fehlender oder leerer <title>. Diese Kategorien sind programmatisch prüfbar, weil es sich um strukturelle, im DOM eindeutig feststellbare Eigenschaften handelt.

Nicht erkennbar sind dagegen Aspekte, die menschliches Urteilsvermögen erfordern: ob die Tab-Reihenfolge bei der Tastaturnavigation logisch dem visuellen Ablauf folgt, ob ein Screenreader eine Seite tatsächlich verständlich vorliest, ob ein vorhandener Alt-Text den Bildinhalt inhaltlich sinnvoll beschreibt statt nur formal zu existieren, oder ob das Fokus-Management in einem Modal-Dialog korrekt funktioniert. Automatisierte Tests sind hier eine notwendige, aber keine hinreichende Ergänzung zu geplanten manuellen Prüfungen.

3. Cypress-Integration mit cypress-axe

Das Paket cypress-axe kapselt axe-core als Cypress-Command und fügt sich nahtlos in bestehende E2E-Suiten ein. Nach jedem cy.visit() muss cy.injectAxe() aufgerufen werden, um die axe-core-Runtime in die Seite einzuschleusen, bevor cy.checkA11y() die eigentliche Prüfung startet. Entscheidend ist, Checks nicht nur einmal pro Seite, sondern nach jeder relevanten Interaktion auszuführen, etwa nach dem Öffnen eines Dropdowns, einer Formularvalidierung oder einem Modal, da genau diese dynamischen Zustände die häufigsten Fundstellen liefern.

Ein zweiter wichtiger Punkt: Wird der DOM durch ein Framework wie Alpine.js komplett neu gerendert, muss cy.injectAxe() erneut aufgerufen werden, weil die vorherige Injektion sonst verloren geht. Der optionale vierte Parameter von cy.checkA11y() steuert, ob gefundene Violations den Test sofort fehlschlagen lassen oder zunächst nur in der Konsole protokolliert werden, was besonders in der Einführungsphase hilfreich ist.


// cypress/e2e/accessibility/checkout.cy.js
describe('Checkout accessibility', () => {
  beforeEach(() => {
    cy.visit('/checkout');
    cy.injectAxe(); // inject axe-core into the page after each navigation
  });

  it('has no serious or critical violations on the shipping step', () => {
    cy.checkA11y('#checkout-container', {
      runOnly: ['wcag2a', 'wcag2aa', 'wcag21aa'],
    }, null, true); // skipFailures=true, log first before enforcing hard failures
  });

  it('has no violations after opening the payment method dropdown', () => {
    cy.get('[data-test="payment-method-toggle"]').click();
    cy.injectAxe(); // re-inject if Alpine.js replaced the surrounding DOM
    cy.checkA11y('[data-test="payment-methods"]');
  });
});

4. Playwright-Integration mit @axe-core/playwright

Für Playwright bietet @axe-core/playwright eine Fluent-API namens AxeBuilder, die sich direkt an eine geöffnete page-Instanz hängt. Nach page.goto() reicht new AxeBuilder({ page }).analyze(), um eine vollständige Prüfung anzustoßen. Weil Playwright echte Browser-Engines steuert, Chromium, Firefox und WebKit, lassen sich Accessibility-Prüfungen zusätzlich cross-browser ausführen, was relevant ist, da ARIA-Unterstützung und Fokus-Verhalten zwischen Engines durchaus abweichen können.

withTags() filtert die geprüften WCAG-Kategorien, exclude() klammert unkontrollierbaren Drittanbieter-Code wie Chat-Widgets aus, und include() begrenzt die Prüfung auf einen bestimmten Container. Die vollständigen Ergebnisse lassen sich über testInfo.attach() direkt in den Playwright-HTML-Report einbetten, sodass Reviewer Violations inklusive betroffenem Selektor und Hilfe-URL ohne separates Tool einsehen können.


// tests/accessibility/product-page.spec.ts
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('product page has no critical accessibility violations', async ({ page }) => {
  await page.goto('/catalog/product/view/id/123');

  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
    .exclude('#third-party-chat-widget') // exclude uncontrolled third-party markup
    .analyze();

  // Attach the full report to the Playwright HTML report for review
  await test.info().attach('axe-results', {
    body: JSON.stringify(results, null, 2),
    contentType: 'application/json',
  });

  const critical = results.violations.filter(v => v.impact === 'critical' || v.impact === 'serious');
  expect(critical, `Found ${critical.length} serious/critical violations`).toHaveLength(0);
});

5. Severity-Schwellenwerte und Regel-Konfiguration

Jede axe-core-Violation trägt ein impact-Level: minor, moderate, serious oder critical. Nicht jedes Team möchte den Build wegen eines minor-Kontrastfehlers auf einer selten besuchten Unterseite blockieren. Über die rules-Konfiguration lassen sich einzelne Regeln gezielt aktivieren oder deaktivieren, über runOnly mit Tags wie wcag2a, wcag2aa, wcag21aa oder best-practice lässt sich der geprüfte Regelsatz präzise auf die eigenen Compliance-Ziele zuschneiden.

Ein pragmatischer Kompromiss, der sich in der Praxis bewährt hat: Der CI-Build schlägt nur bei serious- und critical-Violations fehl, während moderate und minor geloggt und in einem Dashboard gesammelt, aber nicht blockierend behandelt werden. So bleibt die Team-Velocity erhalten, ohne die schwerwiegendsten Barrieren zu übersehen.


{
  "rules": {
    "color-contrast": { "enabled": true },
    "landmark-one-main": { "enabled": false },
    "region": { "enabled": false }
  },
  "runOnly": {
    "type": "tag",
    "values": ["wcag2a", "wcag2aa", "wcag21aa", "best-practice"]
  },
  "resultTypes": ["violations"],
  "reporter": "v2"
}

6. CI-Pipeline: Nur neue Violations blockieren, nicht Legacy-Debt

Das größte Hindernis bei der Einführung von automatisierten Accessibility-Tests: Gewachsene Anwendungen haben oft hunderte bestehende Violations. Ein sofortiger harter Gate-Check würde jeden Build brechen und das Team eher dazu verleiten, den Check komplett zu deaktivieren, als ihn ernst zu nehmen. Der bewährte Weg ist stattdessen ein Baseline-Snapshot: Der aktuelle Violations-Stand wird als JSON-Datei ins Repository committet, und die CI vergleicht bei jedem Build nur die Differenz gegen diese Baseline.

Neue Violations, die nicht in der Baseline enthalten sind, blockieren den Merge sofort. Bereits bekannte Legacy-Violations werden separat getrackt und in eigenen Tickets priorisiert abgebaut, ohne den täglichen Entwicklungsfluss zu stören. Jede Regel-ID und jeder betroffene Selektor dient dabei als Fingerprint für den Abgleich, sodass auch punktuelle Verbesserungen aus der Baseline entfernt werden können, sobald sie behoben sind.


# .github/workflows/accessibility.yml
name: Accessibility Regression Check
on: [pull_request]

jobs:
  axe-baseline-diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run build
      - name: Run axe-core against staging pages
        run: npm run test:a11y -- --json > current-violations.json
      - name: Compare against committed baseline
        run: |
          node scripts/diff-a11y-baseline.js \
            --baseline .a11y-baseline.json \
            --current current-violations.json \
            --fail-on-new
      - name: Upload violation report
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: accessibility-report
          path: current-violations.json

7. Magento- und Hyvä-spezifische Accessibility-Probleme

In Hyvä-Shops findet axe-core wiederkehrende Muster: unzureichender Farbkontrast durch Tailwind-Utility-Klassen wie text-gray-400 auf weißem Hintergrund, die häufig unter dem geforderten Kontrastverhältnis von 4,5:1 liegen. Ebenso häufig: Icon-only Buttons wie das Mini-Cart-Symbol, der Such-Toggle oder das Wishlist-Herz ohne aria-label. Für sehende Nutzer ist die Funktion durch das Icon erkennbar, für Screenreader-Nutzer bleibt der Button ohne zugängliches Label ein bedeutungsloses "Button, nicht beschriftet".

Ein Alpine.js-spezifisches Problem betrifft x-show: Die Direktive blendet Elemente nur per CSS display: none aus, während der Inhalt bei fehlerhafter Timing-Konfiguration oder während Transitions weiterhin im Accessibility-Tree vorhanden und teils sogar fokussierbar bleibt. x-cloak in Kombination mit x-if, das Elemente vollständig aus dem DOM entfernt statt sie nur zu verstecken, vermeidet dieses Problem zuverlässig. Im Checkout sind zudem Formularfelder ohne <label for>-Verknüpfung ein Dauerbrenner, besonders bei individuell gestylten Custom-Select-Komponenten, bei denen ein reiner placeholder fälschlich als ausreichend angesehen wird.


<!-- Before: icon-only button without aria-label, x-show leaves content in the a11y tree -->
<button @click="miniCartOpen = !miniCartOpen">
    <svg class="w-6 h-6" ...></svg>
</button>
<div x-show="miniCartOpen" class="absolute right-0 ...">
    ...
</div>

<!-- After: accessible label, x-cloak avoids the flash, x-if removes hidden content from the DOM -->
<button
    @click="miniCartOpen = !miniCartOpen"
    :aria-expanded="miniCartOpen.toString()"
    aria-label="{{ __('Open mini cart') }}"
>
    <svg class="w-6 h-6" aria-hidden="true" ...></svg>
</button>
<template x-if="miniCartOpen">
    <div x-cloak class="absolute right-0 ..." role="dialog" aria-label="{{ __('Mini cart') }}">
        ...
    </div>
</template>

<!-- Checkout input without an associated label triggers axe-core's "label" rule -->
<input type="text" id="street_1" name="street[0]" placeholder="{{ __('Street Address') }}">
<!-- Fix: explicit label, a placeholder alone is not an accessible name -->
<label for="street_1" class="sr-only">{{ __('Street Address') }}</label>
<input type="text" id="street_1" name="street[0]">

8. Manuelle Tests ergänzen: Tastatur, Screenreader, Lesereihenfolge

Ein Tastatur-only-Durchlauf gehört zu den günstigsten und wirkungsvollsten manuellen Tests: Den kompletten Checkout-Flow ausschließlich mit Tab, Shift+Tab, Enter und Escape durchklicken. Jedes interaktive Element muss erreichbar sein und einen sichtbaren Fokus-Ring erhalten, keine Fokus-Falle darf den Nutzer in einem Modal gefangen halten, und die Tab-Reihenfolge muss dem visuellen Ablauf der Seite entsprechen.

Ein Screenreader-Spotcheck mit NVDA unter Windows oder VoiceOver unter macOS und iOS auf kritischen Seiten wie Produktdetail und Checkout deckt auf, ob Live-Ankündigungen etwa bei Warenkorb-Updates (aria-live) tatsächlich vorgelesen werden und ob die Lesereihenfolge dem visuellen Layout entspricht, was axe-core prinzipbedingt nicht bewerten kann. Diese Prüfungen lassen sich nicht vollständig automatisieren, sollten aber als fester Bestandteil in Sprint-Reviews oder als quartalsweiser Audit im Kalender stehen, statt nur bei Beschwerden reaktiv angegangen zu werden.

9. Testverfahren im Vergleich

Automatisierte und manuelle Verfahren decken unterschiedliche Bereiche ab und ergänzen sich, statt sich zu ersetzen. Die folgende Übersicht zeigt, wofür sich welches Verfahren eignet und wo die jeweiligen Grenzen liegen.

Testverfahren Automatisierbar Typisches Manko Empfohlener Einsatz
axe-core (Cypress/Playwright) Ja, vollautomatisch Erkennt nur ca. 30-40 % der WCAG-Kriterien Bei jedem Pull Request / Build
Lighthouse Accessibility Audit Teilautomatisiert Nur Einzelseiten-Snapshot, keine Interaktions-States Schnelle Stichproben ergänzend zu axe-core
Manueller Tastatur-Test Nein, manuell Zeitaufwendig, nicht CI-tauglich Vor jedem Major Release
Screenreader-Test (NVDA/VoiceOver) Nein, manuell Erfordert geschulte Tester Quartalsweise auf kritischen Flows
Visuelles Fokus-Review Nein, manuell Wird bei Custom-Components leicht übersehen Bei jedem neuen UI-Pattern
Contrast-Checker Browser-Plugin Teilautomatisiert Prüft nur den sichtbaren Viewport Ergänzend zur axe-core Kontrastregel

In der Praxis ergänzen sich automatisierte und manuelle Verfahren: axe-core deckt strukturelle Fehler bei jedem Commit ab, während geplante manuelle Audits die verbleibenden 60 bis 70 Prozent der WCAG-Kriterien abdecken, die Kontext und menschliches Urteilsvermögen erfordern und sich prinzipiell nicht in einer CI-Pipeline abbilden lassen.

Mironsoft

Accessibility-Testing, axe-core-Integration und Hyvä-A11y-Optimierung für Magento-Shops

Automatisierte Accessibility-Tests professionell einführen?

Wir integrieren axe-core in eure Cypress- oder Playwright-Suite, konfigurieren sinnvolle Schwellenwerte und richten eine Baseline ein, die neue Violations blockiert, ohne den Build durch Legacy-Debt zu bremsen.

axe-core CI-Integration

Cypress- oder Playwright-Setup mit sinnvollen Severity-Schwellenwerten

Magento/Hyvä-A11y-Audit

Farbkontrast, Icon-Buttons und Alpine.js-Zustände gezielt prüfen

Monitoring & Baseline-Setup

Baseline-Diffing in der CI/CD-Pipeline ohne Blockade durch Legacy-Debt

10. Zusammenfassung

Accessibility-Testing mit axe-core löst ein klar begrenztes, aber wichtiges Teilproblem: Es fängt strukturelle WCAG-Verstöße wie fehlenden Farbkontrast, fehlende Labels und ARIA-Fehler zuverlässig bei jedem Commit ab, statt sie erst kurz vor einem Release oder gar erst durch Nutzerbeschwerden zu entdecken. Die Integration über cypress-axe oder @axe-core/playwright ist in wenigen Zeilen erledigt, entfaltet ihren Wert aber erst durch sinnvoll konfigurierte Severity-Schwellenwerte und eine Baseline-Strategie, die neue Violations blockiert, ohne an bestehender Legacy-Debt zu scheitern.

Entscheidend ist das realistische Erwartungsmanagement: axe-core deckt etwa 30 bis 40 Prozent der WCAG-Kriterien ab, der Rest, etwa Tastaturnavigation, Screenreader-Verständlichkeit und sinnvolle Lesereihenfolge, bleibt Aufgabe geplanter manueller Audits. In Magento- und Hyvä-Shops treffen automatisierte Checks besonders häufig auf Tailwind-Kontrastfehler, fehlende Labels bei Icon-Buttons und Alpine.js-Zustände, die trotz visueller Unsichtbarkeit im Accessibility-Tree verbleiben.

Accessibility-Testing mit axe-core - Das Wichtigste auf einen Blick

Abdeckung realistisch einschätzen

axe-core findet ca. 30-40 % der WCAG-Kriterien automatisiert, der Rest braucht manuelle Prüfung.

CI ohne Legacy-Blockade

Baseline-Diffing blockiert nur neue Violations, bekannte Altlasten werden separat abgebaut.

Magento/Hyvä-Fallstricke

Tailwind-Kontrast, Icon-Buttons ohne aria-label und x-show-Zustände gezielt prüfen.

Manuelle Tests einplanen

Tastatur- und Screenreader-Checks fest in Sprint-Reviews oder Quartals-Audits verankern.

11. FAQ: Accessibility-Testing automatisieren mit axe-core

1Was ist axe-core und wie unterscheidet es sich von anderen Accessibility-Tools?
Open-Source-Testengine von Deque Systems, prüft den gerenderten DOM zur Laufzeit. Grundlage für Lighthouse und WAVE, aber direkt als npm-Bibliothek in Cypress oder Playwright einbindbar.
2Welcher Anteil der WCAG-Kriterien lässt sich mit axe-core automatisch testen?
Etwa 30 bis 40 Prozent, darunter Farbkontrast, fehlende Alt-Texte, fehlende Formular-Labels und ARIA-Fehler. Der Rest erfordert manuelle Prüfung.
3Wie integriere ich axe-core in Cypress?
Mit cypress-axe: cy.injectAxe() nach jedem cy.visit(), danach cy.checkA11y() nach jedem relevanten UI-Zustand wie geöffneten Dropdowns oder Modals.
4Wie integriere ich axe-core in Playwright?
Mit @axe-core/playwright und der AxeBuilder-Klasse: new AxeBuilder({ page }).analyze() nach page.goto(). withTags() und exclude() steuern den Prüfumfang.
5Wie verhindere ich, dass axe-core den CI-Build wegen bestehender Altlasten blockiert?
Über einen Baseline-Snapshot: CI prüft nur die Differenz gegen die committete Baseline. Neue Violations blockieren, Legacy-Violations werden separat abgebaut.
6Welche Schweregrade (impact levels) kennt axe-core?
minor, moderate, serious und critical. Üblich: Build failt nur bei serious/critical, moderate/minor werden geloggt statt zu blockieren.
7Welche Magento/Hyvä-spezifischen Accessibility-Probleme findet axe-core häufig?
Unzureichender Farbkontrast bei Tailwind-Klassen, Icon-only Buttons wie Mini-Cart ohne aria-label, sowie Checkout-Formularfelder ohne zugeordnetes Label.
8Warum bleibt manuelles Testen trotz axe-core notwendig?
axe-core kann subjektive Aspekte wie sinnvolle Alt-Texte, logische Tastatur-Reihenfolge oder Fokus-Management nicht bewerten - das erfordert menschliches Urteilsvermögen.
9Kann axe-core auch dynamische Alpine.js-Zustände wie x-show prüfen?
Ja, da axe-core den zur Laufzeit gerenderten DOM prüft. Check nach jeder Zustandsänderung erneut auslösen, x-show ggf. durch x-if ersetzen.
10Ersetzt eine bestandene axe-core-Prüfung eine rechtliche Konformität nach BFSG/WCAG?
Nein. Deckt nur einen Teil der WCAG-Kriterien ab und ersetzt keine vollständige manuelle Konformitätsprüfung für eine rechtssichere BFSG-Erklärung.