Note 07 / Barrierefreiheit
Playwright und axe: ein Test, der rot werden kann
Barrierefreiheit auf der gerenderten Seite prüfen. Desktop und Mobile scannen und den Fehlerpfad gezielt nachweisen.
Quelltext ist noch keine gerenderte Seite
Ein Template kann semantisch korrekt aussehen und im Browser trotzdem Text mit zu wenig Kontrast oder einen namenlosen Button zeigen. CSS-Kaskade, Menü und Cookie-Banner beeinflussen das Ergebnis. Deshalb gehört die Prüfung auf die ausgelieferte Seite, mit den tatsächlich geladenen Styles und Schriften.
Der folgende kleine Check besucht dieselbe URL in zwei Ansichten, führt axe aus und setzt bei serious oder critical den Exit-Code auf Fehler. Zusätzlich prüft er einen über den Viewport hinausragenden Seiteninhalt. Die Breiten sind Testbedingungen, keine Aussage darüber, welche Geräte Ihre Besucher verwenden.
Ein ausführbarer Minimalcheck
Speichern Sie den Code als axe-check.mjs. Installieren Sie playwright und axe-core, anschließend den Chromium-Browser mit npx playwright install chromium. Der Aufruf lautet node axe-check.mjs http://127.0.0.1:8769/engineering/. Die geprüfte URL muss eine echte HTML-Seite liefern; auch HTTP-Fehler führen zum Abbruch.
import { chromium } from 'playwright';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const browser = await chromium.launch();
let failed = false;
try {
for (const width of [1440, 390]) {
const page = await browser.newPage({ viewport: { width, height: 900 } });
const response = await page.goto(process.argv[2], { waitUntil: 'load' });
if (!response?.ok()) throw new Error(`HTTP ${response?.status()}`);
await page.evaluate(() => document.fonts.ready);
await page.addScriptTag({ path: require.resolve('axe-core/axe.min.js') });
const results = await page.evaluate(() => axe.run(document));
const blocking = results.violations.filter(
issue => ['serious', 'critical'].includes(issue.impact)
);
const overflow = await page.evaluate(
() => document.documentElement.scrollWidth > innerWidth
);
console.log(JSON.stringify({ width, blocking, overflow,
incomplete: results.incomplete }));
failed ||= blocking.length > 0 || overflow;
await page.close();
}
} finally {
await browser.close();
}
process.exitCode = failed ? 1 : 0;
Den roten Pfad absichtlich auslösen
Der lokale Funktionstest serviert eine kleine zugängliche Fixture und danach dieselbe Fixture mit einem Button ohne zugänglichen Namen. Der erste Aufruf muss erfolgreich enden; der zweite muss fehlschlagen und button-name melden. Damit ist geprüft, dass der Prozess nicht bloß axe-Ergebnisse protokolliert und trotzdem grün endet. Eine weitere Fixture liefert einen HTTP-Fehler.
Für die Engineering-Seiten erweitert der Verifikator diesen Minimalcheck um alle Sprachfassungen, Banner offen und geschlossen, Linkziele, SEO und Codekontrast. Die automatische Prüfung und die gezielte Manipulation laufen nur lokal. Sie verändern keine öffentlich ausgelieferte Seite.
Automatische Prüfungen haben Grenzen
Keine serious- oder critical-Befunde bedeutet nicht vollständige WCAG-Konformität. Prüfen Sie die als incomplete gemeldeten Fälle, außerdem Tastaturbedienung, Fokusreihenfolge, Zoom, verständliche Linktexte und die Qualität von Alternativtexten. axe kann nicht beurteilen, ob eine technische Erklärung verständlich oder eine Übersetzung idiomatisch ist.
Der Minimalcheck öffnet keine Menüs und schließt keinen Banner. Für interaktive Zustände brauchen Sie zusätzliche Schritte und erneute Scans. Halten Sie Werkzeugversionen im Lockfile fest und bewahren Sie Findings neben Screenshots auf; ein Screenshot allein beweist weder Kontrast noch Bedienbarkeit.