Eine Organisation mit vierzig eigenständig gepflegten Websites kann nicht vor jeder Veröffentlichung eine Fachperson für eine manuelle Prüfung nach den Web Content Accessibility Guidelines (WCAG) buchen. Trotzdem gilt die Pflicht unabhängig von der Betriebsgröße: Seit dem 28. Juni 2025 verlangt die Richtlinie (EU) 2019/882, bekannt als European Accessibility Act, von Herstellern, Diensteanbietern, Importeuren und Händlern bestimmter Produkte und Dienstleistungen barrierefreie digitale Angebote. Die Mitgliedstaaten setzen die Vorgabe mit eigenen Bußgeldern und teils mit Marktrücknahmen durch. Ein manueller Rundgang durch jede Seite jeder Website lässt sich bei dieser Menge an Adressen nicht regelmäßig wiederholen, selbst wenn das nötige Budget vorhanden wäre. Automatisierung wird damit zur einzig praktikablen Antwort.
axe-core setzt genau an dieser Stelle an. Die quelloffene Prüf-Engine des Herstellers Deque steckt bereits in den Chrome-DevTools, in Lighthouse und in zahlreichen Testframeworks, aktuell in Version 4.13.0 vom 5. August 2026. Über die Bindung @axe-core/playwright lässt sie sich in einen echten Playwright-Test einbauen und in der Continuous-Integration-Pipeline (CI), also der automatisierten Kette aus Test- und Buildschritten bei jedem Codewechsel, gegen jede Seite jeder betreuten Website laufen lassen, lange bevor ein Mensch die Seite überhaupt öffnet. Was das Werkzeug dabei zuverlässig findet und was es bewusst offen lässt, entscheidet darüber, wie ein solcher Aufbau in der Praxis aussehen muss.
Systemvoraussetzungen
Ein Aufbau für viele Websites gleichzeitig braucht wenige, aber aktuelle Bausteine. Die folgende Tabelle nennt sie mit der Version, die zum Zeitpunkt dieses Artikels aktuell ist.
| Komponente | Version oder Anforderung | Zweck |
|---|---|---|
| axe-core | Version 4.13.0, veröffentlicht am 5. August 2026 | Regel-Engine, die den gerenderten DOM nach WCAG-Mustern durchsucht |
| @axe-core/playwright | Version 4.13.0, folgt der Versionsnummer von axe-core | Bindung, die axe-core über die Klasse AxeBuilder in einen Playwright-Test einspeist |
| @playwright/test | ab Version 1.62, Node.js in einer aktuell unterstützten Version (22, 24 oder 26) | Test-Runner, der den Browser steuert und die Seite lädt |
| GitHub Actions Checkout | actions/checkout@v4 oder neuer | Holt den Code in der Pipeline, Grundlage für jeden Testlauf |
| WCAG-Tag-Auswahl | zum Beispiel wcag2a, wcag2aa, wcag21aa | Legt fest, gegen welche Konformitätsstufe axe-core prüft |
| Pflicht-Statuscheck | im Repository für den Zielzweig hinterlegt | Verhindert, dass ein fehlgeschlagener Scan trotzdem gemergt wird |
Systemarchitektur
Ein Verstoß ist in diesem Zusammenhang jede Stelle im gerenderten HTML, die eine automatisiert prüfbare WCAG-Regel verletzt: ein Bild ohne Alt-Attribut, ein Formularfeld ohne zugehöriges Label, eine Überschriftenebene, die eine Stufe überspringt, oder ein Text, dessen Kontrast zum Hintergrund unter dem geforderten Verhältnis liegt. axe-core selbst startet keinen eigenen Browser, sondern läuft als JavaScript innerhalb der bereits geladenen Seite und durchsucht von dort aus den Document Object Model (DOM), also die Baumstruktur, in die ein Browser eine Webseite beim Rendern übersetzt. Die Bindung @axe-core/playwright übernimmt das Einschleusen: Sie öffnet die Seite mit Playwright, fügt das axe-core-Skript ein und liest über dessen zentrale Methode analyze() das Ergebnis wieder aus. Von Hand geht das bei vierzig Websites nicht.
flowchart TD
A[Playwright oeffnet eine Seite der Website] --> B[AxeBuilder injiziert axe-core in die Seite]
B --> C[axe-core durchsucht den gerenderten DOM]
C --> D[Die Methode analyze sammelt jeden Verstoss mit seiner Impact-Stufe]
D --> E{Ist die Impact-Stufe kritisch oder schwerwiegend}
E -->|Ja| F[Der CI-Job schlaegt fehl und blockiert den Merge]
E -->|Nein| G[Der Verstoss wird nur protokolliert]
F --> H[Die zustaendige Person behebt den Verstoss]
G --> I[Der Bericht steht im Pull Request]
H --> J[Der naechste Lauf prueft die Seite erneut]
I --> J
Der Ablauf trennt zwei Entscheidungen, die sonst leicht vermischt werden. Erstens: Welche Regeln überhaupt laufen, gesteuert über die WCAG-Stufen, die withTags() auswählt. Zweitens: Welcher Fund den Build stoppt, gesteuert über die Impact-Stufe, die axe-core jedem Verstoß automatisch mitgibt: critical, serious, moderate oder minor. Ein Kontrastfehler auf einer selten besuchten Unterseite durchläuft dieselbe Regelprüfung wie ein fehlendes Formular-Label auf der Startseite, aber nicht dieselbe Konsequenz für den Merge.
Schritt-für-Schritt-Umsetzung
Playwright-Test mit axe-core einrichten
Der erste Schritt bindet axe-core in einen normalen Playwright-Test ein. Die Bindung @axe-core/playwright installiert sich wie jedes andere Testpaket und stellt die Klasse AxeBuilder bereit, die eine bereits geöffnete Playwright-Seite entgegennimmt.
npm install --save-dev @axe-core/playwright @playwright/test
import { test, expect } from '@playwright/test';
import { AxeBuilder } from '@axe-core/playwright';
test('Startseite verletzt keine kritischen WCAG-Regeln', async ({ page }) => {
await page.goto('https://www.beispiel-domain.de/');
const ergebnis = await new AxeBuilder({ page }).analyze();
console.log(ergebnis.violations.length, 'automatisiert pruefbare Befunde insgesamt');
expect(ergebnis.violations).toEqual([]);
});
Dieser erste Test ist bewusst noch grob: Er wertet jeden gefundenen Verstoß gleich, unabhängig von dessen Schwere. Für den produktiven Einsatz braucht es zwei Verfeinerungen, die die nächsten beiden Schritte liefern.
WCAG-Stufen mit withTags auswählen
Die Methode withTags() schränkt den Scan auf die Regeln ein, die zu bestimmten WCAG-Konformitätsstufen gehören. axe-core kennt dafür feste Bezeichner: wcag2a und wcag2aa für WCAG 2.0, wcag21a und wcag21aa für die Ergänzungen aus WCAG 2.1, dazu wcag22aa für WCAG 2.2 sowie best-practice für Regeln ohne direkten WCAG-Bezug. Für die meisten Organisationen ist die Kombination aus Stufe A und AA die richtige Wahl, weil die europäische Norm EN 301 549 für Webinhalte genau diese Konformitätsstufe WCAG 2.1 AA vorgibt und damit die technische Richtschnur für den European Accessibility Act bildet. Zusätzlich lassen sich mit include() und exclude() einzelne Bereiche der Seite gezielt ein- oder ausschließen, etwa um ein eingebettetes Drittanbieter-Widget aus der eigenen Verantwortung herauszuhalten.
const ergebnis = await new AxeBuilder({ page })
.include('main')
.exclude('.drittanbieter-widget')
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
Build nur bei kritischen Verstößen stoppen
Jeder von axe-core gemeldete Verstoß trägt ein Feld namens impact mit einem von vier Werten: critical für Fehler, die eine Kernfunktion komplett blockieren, serious für Fehler, die die Nutzung erheblich erschweren, sowie moderate und minor für Fälle mit spürbar geringerer Auswirkung. Ein CI-Gate, das jeden Fund gleich behandelt, blockiert Merges schon wegen kleinerer Funde. Das Team ignoriert es sonst. Sinnvoller ist ein Filter, der nur bei critical und serious fehlschlägt und den Rest lediglich protokolliert.
const alleBefunde = ergebnis.violations;
const kritisch = alleBefunde.filter(
(verstoss) => verstoss.impact === 'critical' || verstoss.impact === 'serious'
);
const restliche = alleBefunde.filter(
(verstoss) => verstoss.impact === 'moderate' || verstoss.impact === 'minor'
);
if (restliche.length > 0) {
console.warn(restliche.length, 'nicht blockierende Befunde, siehe Testbericht');
}
expect(kritisch).toEqual([]);
Alle Seiten aller Websites per Matrix scannen
Ein einzelner Test gegen eine einzelne Startseite deckt bei vierzig eigenständigen Websites nur einen Bruchteil der tatsächlichen Fläche ab. GitHub Actions löst das über eine Matrix-Strategie: Ein Job läuft dabei einmal je Eintrag in einer Liste, hier je betreuter Website, wobei jeder Lauf unabhängig von den anderen startet und im Ergebnis einzeln angezeigt wird.
name: accessibility-scan
on:
pull_request:
push:
branches: [main]
schedule:
- cron: "0 5 * * 1"
jobs:
axe-scan:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
website: [website-a, website-b, website-c]
defaults:
run:
working-directory: sites/${{ matrix.website }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test tests/a11y.spec.ts
Der Workflow läuft bei jedem Pull Request, zusätzlich bei jedem Push auf den Hauptzweig und einmal wöchentlich nach Zeitplan, damit ein neu gefundener Verstoß nicht allein von einer laufenden Codeänderung abhängt. fail-fast: false sorgt dafür, dass ein fehlgeschlagener Scan bei einer Website die Prüfung der übrigen nicht abbricht, sodass ein einziger Pull Request am Ende den vollständigen Zustand aller Websites zeigt.
Häufige Fehlerquellen
- Alt-Text ist vorhanden, aber nichtssagend. axe-core meldet ein Bild mit dem Attribut
alt="bild1"nicht als Fehler, weil die zugehörige Regel nur prüft, ob das Attribut existiert, nicht, ob sein Inhalt das Bild beschreibt. Fix: den Alt-Text bei jeder Bildänderung stichprobenartig gegen das Bild lesen, unabhängig vom automatisierten Ergebnis. - Ausgeblendete Bereiche bleiben ungeprüft. Ein Navigationsmenü, das erst nach einem Klick sichtbar wird, oder ein Dialog, der im Ruhezustand der Seite ausgeblendet ist, taucht im Scan nicht auf, weil axe-core nur den DOM zum Zeitpunkt von
analyze()durchsucht. Fix: den jeweiligen Zustand im Test gezielt öffnen und dafür einen eigenen Aufruf vonanalyze()schreiben. - Der Build stoppt bei jedem Fund statt nur bei schweren. Ein Vergleich von
violationsdirekt gegen ein leeres Array blockiert einen Merge schon wegen eines einzelnenminor-Befundes. Fix: ausschließlich nachcriticalundseriousfiltern und den Rest in den Testbericht schreiben. - Das incomplete-Ergebnis bleibt ungelesen. Neben
violationsliefert axe-core ein zweites Feld namensincompletefür Elemente, bei denen sich ein Verstoß nicht sicher feststellen ließ. Wer nurviolationsauswertet, übersieht ausgerechnet die Fälle, die eine manuelle Prüfung am nötigsten haben. Fix:incompleteregelmäßig durchsehen, auch wenn es den Build nicht stoppt. - Dynamischer Inhalt ist beim Scan noch nicht geladen. Läuft
analyze()unmittelbar nachpage.goto(), bevor nachgeladene Inhalte oder ein Cookie-Banner vollständig gerendert sind, prüft axe-core einen unvollständigen Zustand der Seite. Fix: vor dem Scan gezielt auf ein Element warten, statt sich auf eine feste Wartezeit zu verlassen. - Nur die Startseite jeder Website steht im Test. Bei vierzig Websites mit je einer geprüften Seite bleiben hunderte Unterseiten ungeprüft, obwohl Formulare und Navigationselemente dort oft abweichen. Fix: pro Website eine Liste der wichtigsten Routen pflegen und jede Route einzeln in der Matrix oder einer Schleife prüfen.
Häufig gestellte Fragen
Wie viel Prozent der WCAG-Probleme erkennt axe-core automatisch?
axe-core erkennt nach einer eigenen Auswertung seines Herstellers Deque im Schnitt 57 Prozent aller Barrierefreiheitsprobleme automatisch. Die Zahl stammt aus einer Analyse von mehr als 13.000 geprüften Seiten und knapp 300.000 gefundenen Einzelproblemen aus echten Erstprüfungen. Für Fälle, bei denen sich ein Verstoß nicht sicher feststellen lässt, liefert axe-core zusätzlich ein incomplete-Ergebnis, das eine manuelle Prüfung verlangt, statt einen falschen Fehlalarm auszugeben. Die verbleibenden gut 40 Prozent betreffen vor allem Kriterien, die ein Urteil erfordern, etwa ob eine Beschreibung inhaltlich verständlich ist oder eine Screenreader-Ansage tatsächlich Sinn ergibt.
Kann ein automatisierter axe-core-Scan eine manuelle Barrierefreiheitsprüfung ersetzen?
Ein automatisierter axe-core-Scan kann eine manuelle Barrierefreiheitsprüfung nicht ersetzen, weil er nur Kriterien bewerten kann, die sich rein aus dem Code ableiten lassen. axe-core stellt fest, ob ein Bild ein Alt-Attribut trägt, aber nicht, ob dessen Text das Bild inhaltlich zutreffend beschreibt, und es beurteilt nicht, ob eine reine Tastaturbedienung in einer sinnvollen Reihenfolge durch die Seite führt. Ein Screenreader-Test und eine Prüfung mit ausschließlicher Tastaturbedienung bleiben deshalb Teil jeder ernsthaften Barrierefreiheitsprüfung, besonders bei Formularen, Menüs und Dialogen. Der automatisierte Scan übernimmt dafür die laufende Kontrolle bei jeder einzelnen Änderung, statt nur bei einem jährlichen Audit zu greifen.
Wie stoppt man den CI-Build nur bei kritischen Barrierefreiheitsfehlern?
Der CI-Build stoppt nur bei kritischen Barrierefreiheitsfehlern, wenn die Auswertung die von axe-core gemeldeten Verstöße nach ihrer Impact-Stufe filtert, statt jeden Fund gleich zu behandeln. axe-core vergibt jedem Verstoß eine von vier Stufen: critical, serious, moderate oder minor. Ein Test wertet ausschließlich die ersten beiden Stufen als Fehlschlag aus und schreibt den Rest in den Testbericht, ohne den Merge zu blockieren. So bleibt die Pflichtprüfung streng bei Verstößen, die die Bedienung tatsächlich verhindern, ohne dass ein Team wegen einer Formsache jede Woche einen roten Build ignoriert.
Ab wann gilt die europäische Pflicht zur digitalen Barrierefreiheit für Websites?
Die europäische Pflicht zur digitalen Barrierefreiheit gilt seit dem 28. Juni 2025 für neue Produkte und Dienstleistungen, die unter die Richtlinie (EU) 2019/882 fallen, bekannt als European Accessibility Act. Betroffen sind unter anderem Hersteller, Diensteanbieter, Importeure und Händler in Bereichen wie E-Commerce, Bankdienstleistungen, E-Books und der Fahrgastinformation im Personenverkehr, unabhängig davon, in welchem Land das Unternehmen seinen Sitz hat. Als technische Richtschnur gilt in der Praxis die Norm EN 301 549, die für Webinhalte die Konformitätsstufe WCAG 2.1 AA festlegt. Jeder Mitgliedstaat setzt die Richtlinie mit eigenen Fristen, Kontrollen und Bußgeldern durch, weshalb eine verbindliche Einschätzung der eigenen Pflichten eine fachkundige rechtliche Prüfung im Einzelfall braucht.