Barrierefreiheit mit Claude Code: HTML, ARIA, axe und manuelle Prüfung
Praxis-Workflow für Barrierefreiheit mit Claude Code: HTML, Tastatur, Formulare, Fokus, axe und Screenreader.
Barrierefreiheit ist keine Aufgabe, bei der man kurz vor dem Release ein Tool startet und ein paar Warnungen behebt. Sie beginnt bei semantischem HTML, geht weiter über Tastaturbedienung, Fokusführung, Formularfehler und Farbkontrast und endet erst nach automatisierten Checks und manueller Prüfung mit Screenreader.
Claude Code kann diesen Ablauf stark beschleunigen. Dafür muss die Aufgabe aber präzise sein. Ein Prompt wie “mach die Komponente barrierefrei” führt leicht zu einem div mit ARIA, aber ohne Enter- oder Space-Verhalten, zu einem Modal ohne saubere Fokusrückgabe oder zu Fehlermeldungen, die sichtbar sind, aber nicht vorgelesen werden.
Als Grundlage dienen offizielle Quellen: W3C WCAG 2.2 als praktisches Ziel, der WAI-ARIA Authoring Practices Guide für Widget-Patterns, MDN ARIA für “semantisches HTML zuerst”, die Deque axe-core Dokumentation für automatisierte Tests und die Claude Code Dokumentation für die Tool-Grundlagen.
Ziel vor dem Patch festlegen
“Barrierefreiheit verbessern” ist zu ungenau. Ein brauchbares Ziel lautet: WCAG 2.2 AA als Arbeitsstandard, semantisches HTML vor ARIA, Kernpfade vollständig per Tastatur bedienbar, sichtbarer Fokus, verständliche Fehler und Nachweis durch automatische plus manuelle Prüfung.
| Bereich | Praktischer Maßstab | Typischer Fehler |
|---|---|---|
| Semantisches HTML | button, a, form, label, main, nav korrekt einsetzen | Klickbares div ersetzt native Kontrolle |
| Tastatur | Tab, Shift+Tab, Enter, Space und Escape decken den Hauptfluss ab | Modal schließt nur mit Maus |
| Fokus | Fokus geht in neue UI und kehrt zum Auslöser zurück | Fokus landet hinter dem Dialog |
| ARIA | Nur ergänzen, wenn HTML den Zustand nicht ausdrückt | aria-label verdeckt fehlendes sichtbares Label |
| Farbe | Text, Kontrollen, Fehler und Fokus sind sichtbar | Fehler wird nur durch Rot vermittelt |
| Formulare | Labels, Hinweise, Invalid-State und Fehler sind mit Inputs verbunden | Fehler wird angezeigt, aber nicht angekündigt |
| Tests | axe plus Tastatur und Screenreader | Keine automatischen Verstöße wird als vollständiger Audit missverstanden |
MDN empfiehlt, native HTML-Elemente zu verwenden, wenn sie die benötigte Semantik und das Verhalten bereits liefern. Claude Code sollte deshalb zuerst Struktur reparieren und ARIA nur für fehlende Zustände wie aria-invalid, aria-expanded oder aria-modal ergänzen.
Sicherer Claude-Code-Prompt
Ein guter Prompt ist eine Review-Anweisung mit Grenzen. Er schützt Business-Inhalte, nennt den Standard und verlangt Belege.
claude <<'PROMPT'
Scope:
- Review only src/components/CheckoutForm.tsx and its tests.
- Do not change pricing copy, analytics events, or unrelated styles.
Accessibility target:
- Use WCAG 2.2 AA as the practical target.
- Prefer semantic HTML before ARIA.
- Add ARIA only when native HTML cannot express the state.
Check these items:
- Labels, descriptions, required state, and validation errors.
- Keyboard operation with Tab, Shift+Tab, Enter, Space, and Escape.
- Focus order, visible focus, and focus return after closing UI.
- Color contrast and non-color error indicators.
- Automated axe check plus manual screen-reader notes.
Output:
- Findings first, with file and line references.
- Minimal patch.
- Commands to verify.
- Any remaining risk.
PROMPT
Findings first ist entscheidend. Claude Code soll erst den Defekt erklären und danach den kleinsten Patch liefern. Das schützt Seiten mit Preisen, CTA, Gumroad-Links oder Analytics-Events vor unnötigen Umbauten. Mehr zu kleinen, überprüfbaren Aufgaben steht in den Claude Code Produktivitätstipps.
Use Case 1: Produkt-CTA oder Artikel-CTA
Ein CTA ist oft Umsatzpfad. Wenn er navigiert, braucht er einen echten Link, nicht eine ganze Karte mit onclick.
<div class="hero-card" onclick="location.href='/en/products'">
<div class="title">Claude Code Templates</div>
<div class="button">Buy now</div>
</div>
Die robuste Variante trennt Überschrift, Nutzen und Ziel.
<section aria-labelledby="templates-heading" class="product-cta">
<h2 id="templates-heading">Reviews mit Claude-Code-Vorlagen verkürzen</h2>
<p>
Kopieren Sie wiederverwendbare Prompts für Implementierung,
Review, Debugging und Dokumentation.
</p>
<a class="primary-link" href="/en/products">
Produktressourcen ansehen
</a>
</section>
Sag Claude Code ausdrücklich, dass Ziel-URLs, Tracking-Attribute und CTA-Text erhalten bleiben müssen. Ein barrierefreier Patch, der den Conversion-Pfad beschädigt, ist kein guter Patch.
Use Case 2: Kontakt- oder Beratungsformular
Formulare verbinden Barrierefreiheit und Umsatz. Wenn Label, Format, Fehler oder Korrekturhinweis unklar sind, bricht der Nutzer die Anfrage ab.
import { FormEvent, useState } from "react";
type Errors = {
name?: string;
email?: string;
};
export function ConsultationForm() {
const [errors, setErrors] = useState<Errors>({});
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = new FormData(event.currentTarget);
const nextErrors: Errors = {};
if (!String(data.get("name") || "").trim()) {
nextErrors.name = "Bitte geben Sie Ihren Namen ein.";
}
if (!String(data.get("email") || "").includes("@")) {
nextErrors.email = "Bitte geben Sie eine gültige E-Mail-Adresse ein.";
}
setErrors(nextErrors);
}
return (
<form aria-labelledby="consultation-title" onSubmit={handleSubmit} noValidate>
<h2 id="consultation-title">Beratungsanfrage</h2>
<div className="field">
<label htmlFor="name">Name</label>
<input
id="name"
name="name"
autoComplete="name"
aria-invalid={errors.name ? "true" : "false"}
aria-describedby={errors.name ? "name-error" : undefined}
/>
{errors.name && (
<p id="name-error" role="alert">
{errors.name}
</p>
)}
</div>
<div className="field">
<label htmlFor="email">E-Mail</label>
<p id="email-help">Nutzen Sie eine Adresse, unter der wir antworten können.</p>
<input
id="email"
name="email"
type="email"
autoComplete="email"
aria-invalid={errors.email ? "true" : "false"}
aria-describedby={
errors.email ? "email-help email-error" : "email-help"
}
/>
{errors.email && (
<p id="email-error" role="alert">
{errors.email}
</p>
)}
</div>
<button type="submit">Anfrage senden</button>
</form>
);
}
Der häufigste Fehler ist eine sichtbare Fehlermeldung ohne Verbindung zum Input. Der zweite ist ein dauerhaft referenziertes Fehler-ID, obwohl das Element noch nicht existiert. Dauerhafte Hilfe bleibt immer verknüpft, Fehler erst im Fehlerfall.
Use Case 3: Modal, Command Palette und Menü
Beim Modal zählt nicht nur die Optik. Der offizielle Dialog Modal Pattern verlangt: Fokus geht hinein, Tab bleibt im Dialog, Escape schließt, Fokus kehrt zum Auslöser zurück.
import { ReactNode, useEffect, useRef } from "react";
type ModalProps = {
open: boolean;
title: string;
onClose: () => void;
children: ReactNode;
};
const focusableSelector = [
"a[href]",
"button:not([disabled])",
"input:not([disabled])",
"select:not([disabled])",
"textarea:not([disabled])",
'[tabindex]:not([tabindex="-1"])',
].join(",");
export function AccessibleModal(props: ModalProps) {
const { open, title, onClose, children } = props;
const dialogRef = useRef<HTMLDivElement>(null);
const previousFocusRef = useRef<HTMLElement | null>(null);
useEffect(() => {
if (!open) return;
previousFocusRef.current = document.activeElement as HTMLElement;
const focusable = dialogRef.current?.querySelectorAll<HTMLElement>(
focusableSelector
);
focusable?.[0]?.focus();
function onKeyDown(event: KeyboardEvent) {
if (event.key === "Escape") onClose();
if (event.key !== "Tab" || !dialogRef.current) return;
const items = [...dialogRef.current.querySelectorAll<HTMLElement>(
focusableSelector
)];
const first = items[0];
const last = items[items.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last?.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first?.focus();
}
}
document.addEventListener("keydown", onKeyDown);
return () => {
document.removeEventListener("keydown", onKeyDown);
previousFocusRef.current?.focus();
};
}, [open, onClose]);
if (!open) return null;
return (
<div className="modal-backdrop">
<div
ref={dialogRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
className="modal-panel"
>
<h2 id="modal-title" tabIndex={-1}>
{title}
</h2>
{children}
<button type="button" onClick={onClose}>
Schließen
</button>
</div>
</div>
);
}
Bei Menüs ist Vorsicht nötig. Der Menu Button Pattern passt zu Befehlsmenüs. Normale Website-Navigation ist oft besser als nav mit Links umgesetzt.
Farbe, Fokus und Mobile
Viele Regressionen entstehen in der Design-Politur: outline: none, graue Texte, Fehler nur in Rot. Sichtbarer Fokus braucht eine klare Alternative.
.primary-link {
background: #0f766e;
border-radius: 6px;
color: #ffffff;
display: inline-flex;
font-weight: 700;
min-height: 44px;
padding: 0.75rem 1rem;
}
.primary-link:focus-visible,
button:focus-visible,
input:focus-visible {
outline: 3px solid #f59e0b;
outline-offset: 3px;
}
.field [role="alert"] {
border-left: 4px solid #b91c1c;
color: #7f1d1d;
margin-top: 0.5rem;
padding-left: 0.75rem;
}
Prüfe auch mobile Breiten. Zu kleine Close-Buttons, Fokus unter Sticky Headern oder zu schmale CTA-Flächen fallen am Desktop schnell durch.
axe und manuelle Prüfung
axe findet strukturelle Fehler schnell. Es beweist nicht, dass Texte verständlich sind oder die Lesereihenfolge für den Geschäftsfluss passt.
npm install -D @axe-core/playwright @playwright/test
npx playwright install --with-deps chromium
import AxeBuilder from "@axe-core/playwright";
import { expect, test } from "@playwright/test";
test("consultation form has no serious accessibility issues", async ({ page }) => {
await page.goto("/contact");
const results = await new AxeBuilder({ page })
.include("main")
.withTags(["wcag2a", "wcag2aa", "wcag22aa"])
.analyze();
expect(results.violations).toEqual([]);
});
Manuell prüfst du zuerst Produkt-CTA, Formular, Checkout, Modal, Navigation und Fehlerkorrektur. Unter Windows ist NVDA ein guter Start, unter macOS VoiceOver.
Konkrete Fehlerliste
role="button"ohne Enter- und Space-Unterstützung.- Icon-Button ohne zugänglichen Namen.
- Nutzloser Alternativtext wie
alt="image". aria-hidden="true"versteckt Modal oder Live Region.- Fehlertext ist sichtbar, aber nicht mit
aria-describedbyverbunden. - Fokusdarstellung wurde entfernt.
- Modal gibt Fokus nicht an den Auslöser zurück.
- Automatischer Check prüft nur Marketingseite, nicht das echte Formular.
Diese Liste kannst du Claude Code direkt nach dem Patch geben und um Datei- und Zeilenangaben bitten.
CTA und Praxisergebnis
Für wiederholbare Workflows starte mit den Claude Code Produkten. Wenn Review- und Debugging-Prompts ständig wiederkehren, helfen die 50 Claude-Code-Prompt-Vorlagen. Für Team-Rollout mit Permissions, Hooks und Verification Receipts ist Beratung sinnvoll.
Für diese Aktualisierung habe ich drei reale Fehler nachgestellt: eine CTA-Karte als klickbares div, ein Formular mit nicht angekündigten Fehlern und ein Modal ohne Fokusrückgabe. Am stabilsten war: Claude Code zuerst Findings liefern lassen, dann minimalen Patch erstellen, danach Tastatur und VoiceOver prüfen. axe hat Strukturfehler gefunden, aber die Verständlichkeit des Flows blieb manuelle Arbeit.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude Code Permission Safety Ladder: Zugriff kontrolliert erweitern
Von read-only zu begrenzten Änderungen, Prüfbefehlen und Deploy-Checks mit klarer Kontrolle.
Claude Code Small PR Proof Pack: kleine Änderungen reviewbar machen
Ein Proof Pack für Claude-Code-PRs: Diff, Checks, öffentliche URL, CTA-Pfad und Rollback.
Claude-Code-Review-Gate vor dem Commit
Vor dem Commit mit Claude Code prüfen: Diff, Build, öffentliche URL, Gumroad-Links, Beratung-CTA, fehlende Tests und fremde Dateien.