Ein umfassender Leitfaden zum Erstellen barrierefreier Websites für alle - mit HTML, CSS, JavaScript, ARIA und modernen Webentwicklungstechniken für WCAG 2.1 Level AA Konformität
Web-Barrierefreiheit stellt sicher, dass Websites, Tools und Technologien so gestaltet und entwickelt werden, dass Menschen mit Behinderungen sie nutzen können. Genauer gesagt: Menschen können das Web wahrnehmen, verstehen, navigieren, mit ihm interagieren und dazu beitragen.
Barrierefreiheit umfasst alle Behinderungen, die den Zugang zum Web beeinträchtigen:
Über Behinderungen hinaus profitieren alle von barrierefreien Websites: ältere Menschen mit veränderten Fähigkeiten, mobile Nutzer bei hellem Sonnenlicht, Menschen mit temporären Beeinträchtigungen (gebrochener Arm, Augenoperation), Nutzer mit langsamen Verbindungen und Menschen in lauten oder ruhigen Umgebungen, in denen sie kein Audio verwenden können.
Die Web Content Accessibility Guidelines (WCAG) 2.1 Level AA sind der international anerkannte Standard für Web-Barrierefreiheit. Sie werden weltweit von Gesetzen referenziert, einschließlich ADA (USA), EAA (EU), BFSG (Deutschland) und vielen anderen.
WCAG 2.1 ist um vier Prinzipien organisiert, bekannt als POUR:
WCAG hat drei Konformitätsstufen: A (niedrigste), AA (mittlere Stufe, gesetzlich in den meisten Rechtsordnungen erforderlich) und AAA (höchste). Dieser Leitfaden konzentriert sich auf Level AA Konformität.
Effektive Barrierefreiheit erfordert, von Anfang an darüber nachzudenken, nicht sie am Ende hinzuzufügen. Befolgen Sie diese Prinzipien:
Semantisches HTML ist der wichtigste Aspekt der Web-Barrierefreiheit. Screen Reader, Tastaturnavigation und Browser-Barrierefreiheitsfunktionen basieren alle auf der richtigen HTML-Struktur.
Verwenden Sie HTML5-Landmark-Elemente, um Seitenbereiche zu definieren. Screen Reader-Nutzer können zwischen Landmarks für schnelle Navigation springen.
<!-- GOOD: Semantic landmarks -->
<header>
<nav>
<!-- Main navigation -->
</nav>
</header>
<main>
<article>
<!-- Primary content -->
</article>
<aside>
<!-- Sidebar/supplementary content -->
</aside>
</main>
<footer>
<!-- Footer content -->
</footer>
<!-- BAD: Generic divs -->
<div class="header">
<div class="nav"></div>
</div>
<div class="content"></div>
<div class="footer"></div>
<main> pro Seite. Sie können mehrere <nav>, <aside> und <section> Elemente haben, beschriften Sie sie aber mit aria-label, wenn es mehrere vom gleichen Typ gibt (z.B. <nav aria-label="Primär"> und <nav aria-label="Fußzeile">).
Überschriften (<h1>-<h6>) erstellen eine Dokumentgliederung. Screen Reader-Nutzer navigieren nach Überschriften, daher ist die richtige Hierarchie entscheidend.
<!-- GOOD: Logical heading hierarchy -->
<h1>Page Title</h1>
<h2>Section 1</h2>
<h3>Subsection 1.1</h3>
<h3>Subsection 1.2</h3>
<h2>Section 2</h2>
<h3>Subsection 2.1</h3>
<!-- BAD: Skipped levels and styling-based choices -->
<h1>Page Title</h1>
<h4>Section (h4 because it looks better)</h4>
<h2>Another Section</h2>
Best Practices:
<h1> pro Seite (typischerweise der Seitentitel)Verwenden Sie geeignetes Listen-Markup für zusammenhängende Elemente. Screen Reader kündigen "Liste mit X Elementen" an und ermöglichen Nutzern, durch Listen zu springen.
<!-- GOOD: Semantic lists --> <ul> <li>Unordered item 1</li> <li>Unordered item 2</li> </ul> <ol> <li>Step 1</li> <li>Step 2</li> </ol> <dl> <dt>Term</dt> <dd>Definition</dd> </dl> <!-- BAD: Fake lists with divs --> <div class="list"> <div class="list-item">• Item 1</div> <div class="list-item">• Item 2</div> </div>
Einer der häufigsten Barrierefreiheitsfehler ist die Verwechslung von Buttons und Links. Sie sind semantisch unterschiedlich und vermitteln assistiven Technologien unterschiedliche Bedeutungen.
| Element | Purpose | Keyboard | Screen Reader |
|---|---|---|---|
<a> |
Navigation (goes somewhere) | Enter to activate | "Link" |
<button> |
Action (does something) | Enter or Space | "Button" |
<!-- GOOD: Proper usage --> <a href="/products">View Products</a> <button type="button" onclick="openModal()">Open Modal</button> <button type="submit">Submit Form</button> <!-- BAD: Misused elements --> <div class="button" onclick="navigate()">Go</div> <a href="#" onclick="doAction()">Click me</a> <button onclick="location.href='/page'">Navigate</button>
div oder span als interaktive Elemente
<div onclick="..."> und <span onclick="..."> sind nicht mit der Tastatur zugänglich, haben keine semantische Bedeutung und erfordern umfangreiches ARIA, um mit assistiven Technologien zu funktionieren. Verwenden Sie immer geeignete <button> oder <a> Elemente. Wenn Sie unbedingt ein div verwenden müssen, fügen Sie role="button", tabindex="0" und Tastatur-Event-Handler hinzu - aber verwenden Sie einfach ein <button>.
Formulare sind entscheidend für die Benutzerinteraktion und müssen vollständig barrierefrei sein. Richtiges Formular-Markup stellt sicher, dass Screen Reader-Nutzer verstehen, was einzugeben ist, und Feedback zu Fehlern erhalten.
Jedes Formulareingabefeld muss ein zugehöriges <label> haben. Labels vergrößern den klickbaren Bereich und liefern wesentliche Informationen für Screen Reader.
<!-- GOOD: Proper label association --> <label for="email">Email Address</label> <input type="email" id="email" name="email" required> <!-- ALSO GOOD: Implicit association --> <label> Password <input type="password" name="password" required> </label> <!-- BAD: No label --> <input type="text" placeholder="Enter name"> <!-- BAD: Placeholder is not a label --> <input type="email" placeholder="Email">
Verwenden Sie aria-describedby, um Anweisungen mit Eingaben zu verknüpfen.
<label for="password">Password</label> <input type="password" id="password" aria-describedby="password-hint" required> <div id="password-hint"> Must be at least 8 characters with 1 number and 1 special character </div>
Stellen Sie klare, barrierefreie Fehlermeldungen bereit, die mit den spezifischen Feldern mit Fehlern verknüpft sind.
<label for="email">Email Address</label> <input type="email" id="email" aria-invalid="true" aria-describedby="email-error"> <div id="email-error" role="alert"> Please enter a valid email address </div>
Best Practices für Fehlermeldungen:
aria-invalid="true" bei Feldern mit Fehlernrole="alert", damit Screen Reader Fehler sofort ankündigenMarkieren Sie Pflichtfelder programmatisch und visuell.
<label for="name"> Full Name <span aria-label="required">*</span> </label> <input type="text" id="name" required aria-required="true"> <!-- OR include "required" in the label text --> <label for="email">Email Address (required)</label> <input type="email" id="email" required>
Verwenden Sie <fieldset> und <legend>, um zusammenhängende Formularelemente zu gruppieren, insbesondere Radiobuttons und Checkboxen.
<fieldset>
<legend>Choose your subscription plan</legend>
<label>
<input type="radio" name="plan" value="free">
Free
</label>
<label>
<input type="radio" name="plan" value="pro">
Pro
</label>
</fieldset>
Verwenden Sie Tabellen für tabellarische Daten, niemals für Layout. Richtiges Tabellen-Markup hilft Screen Reader-Nutzern, Beziehungen zwischen Daten zu verstehen.
<table>
<caption>Sales Report Q1 2025</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Revenue</th>
<th scope="col">Profit</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">January</th>
<td>$50,000</td>
<td>$12,000</td>
</tr>
</tbody>
</table>
Best Practices für Tabellen:
<caption> oder aria-label hinzu, um die Tabelle zu beschreiben<th> für Überschriften mit scope="col" oder scope="row"<thead>, <tbody> und <tfoot>, um Zeilen zu gruppierenheaders-Attribut, um Datenzellen mit mehreren Überschriften zu verknüpfen
Jedes <img> muss ein alt-Attribut haben. Alt-Text sollte den Zweck und Inhalt des Bildes vermitteln, nicht nur beschreiben.
Für Bilder, die Informationen vermitteln, beschreiben Sie Inhalt und Bedeutung.
<!-- GOOD: Descriptive alt text --> <img src="chart.png" alt="Bar chart showing 50% increase in sales from 2024 to 2025"> <!-- BAD: Not descriptive enough --> <img src="chart.png" alt="Chart"> <img src="chart.png" alt="Bar chart">
Für rein dekorative Bilder, die keine Informationen hinzufügen, verwenden Sie leeren Alt-Text, um sie vor Screen Readern zu verbergen.
<!-- GOOD: Empty alt for decorative image --> <img src="divider.png" alt=""> <!-- ALSO GOOD: CSS background image for decorative content --> <div style="background-image: url(decoration.png)"></div>
Für Bilder innerhalb von Links oder Buttons beschreiben Sie die Aktion, nicht das Bild.
<!-- GOOD: Describes action --> <a href="/search"> <img src="search-icon.png" alt="Search"> </a> <button> <img src="close.png" alt="Close dialog"> </button> <!-- BAD: Describes image instead of action --> <a href="/search"> <img src="search-icon.png" alt="Magnifying glass icon"> </a>
Für komplexe Bilder wie Infografiken, Diagramme oder Diagramme stellen Sie sowohl kurzen Alt-Text als auch eine längere Beschreibung bereit.
<img
src="sales-data.png"
alt="Quarterly sales performance graph"
aria-describedby="graph-description">
<div id="graph-description">
<h3>Detailed Description</h3>
<p>The graph shows quarterly sales from Q1 2024 to Q4 2024...</p>
<ul>
<li>Q1: $120,000</li>
<li>Q2: $145,000</li>
...
</ul>
</div>
Alle vorproduzierten Videos mit Audio müssen synchronisierte Untertitel haben. Untertitel kommen gehörlosen Nutzern, Menschen in geräuschempfindlichen Umgebungen, Nicht-Muttersprachlern und Nutzern mit Hörverarbeitungsschwierigkeiten zugute.
<video controls> <source src="video.mp4" type="video/mp4"> <track kind="captions" src="captions-en.vtt" srclang="en" label="English" default> <track kind="captions" src="captions-de.vtt" srclang="de" label="Deutsch"> </video>
Best Practices für Untertitel:
Videoinhalte sollten Audiodeskriptionen haben, die wichtige visuelle Informationen während Pausen im Dialog erzählen.
<video controls> <source src="video.mp4" type="video/mp4"> <track kind="descriptions" src="descriptions.vtt" srclang="en" label="Audio Descriptions"> </video>
Stellen Sie Texttranskripte für reine Audio-Inhalte (Podcasts, Audio-Interviews) und als zusätzliche Alternative für Videos bereit.
<audio controls> <source src="podcast.mp3" type="audio/mpeg"> Your browser does not support the audio element. </audio> <p><a href="transcript.html">Read transcript</a></p>
Stellen Sie sicher, dass Medienplayer barrierefreie Steuerelemente haben:
controls-Attribut), wenn möglichWenn ein Icon rein dekorativ ist (benachbarter Text beschreibt bereits die Aktion), verbergen Sie es vor Screen Readern.
<button>
<svg aria-hidden="true" focusable="false">
<!-- icon markup -->
</svg>
Save
</button>
Wenn ein Icon alleine ohne Text steht, stellen Sie barrierefreien Text bereit.
<button aria-label="Close">
<svg aria-hidden="true" focusable="false">
<!-- X icon -->
</svg>
</button>
<!-- OR use visually hidden text -->
<button>
<svg aria-hidden="true" focusable="false">
<!-- icon -->
</svg>
<span class="sr-only">Close</span>
</button>
Für informative SVG-Bilder verwenden Sie <title> und <desc> Elemente.
<svg role="img" aria-labelledby="chart-title chart-desc"> <title id="chart-title">Sales Growth Chart</title> <desc id="chart-desc">Bar chart showing 25% sales growth from 2024 to 2025</desc> <!-- SVG content --> </svg>
Alle Funktionen müssen über die Tastatur verfügbar sein. Viele Nutzer können aufgrund motorischer Behinderungen keine Maus verwenden, und Power-User bevorzugen oft die Tastaturnavigation.
Standard-Tastaturinteraktionen:
Tastaturnutzer müssen immer wissen, wo sich der Fokus befindet. Entfernen Sie niemals Fokusumrisse ohne alternative Anzeige bereitzustellen.
/* BAD: Removing focus outline entirely */
button:focus {
outline: none;
}
/* GOOD: Custom focus style */
button:focus {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
/* BETTER: Use :focus-visible for keyboard-only focus */
button:focus {
outline: none; /* Remove for mouse clicks */
}
button:focus-visible {
outline: 2px solid #2563eb; /* Show for keyboard */
outline-offset: 2px;
}
Die Tab-Reihenfolge sollte der visuellen Reihenfolge und dem logischen Ablauf folgen. Vermeiden Sie die Verwendung von tabindex-Werten größer als 0.
<!-- GOOD: Natural DOM order determines tab order --> <button>First</button> <button>Second</button> <button>Third</button> <!-- BAD: Positive tabindex disrupts natural order --> <button tabindex="3">Third</button> <button tabindex="1">First</button> <button tabindex="2">Second</button> <!-- ACCEPTABLE: tabindex="-1" for programmatic focus --> <div tabindex="-1" id="error-summary"> Please fix the following errors... </div>
Wenn ein modaler Dialog geöffnet wird, sollte der Fokus in ihn wandern und dort gefangen bleiben, bis er geschlossen wird.
// Focus trap implementation
function trapFocus(element) {
const focusableElements = element.querySelectorAll(
'a[href], button:not([disabled]), textarea, input, select'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey) {
if (document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
}
} else {
if (document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
}
});
firstFocusable.focus();
}
Stellen Sie einen "Zum Hauptinhalt springen"-Link als erstes fokussierbares Element auf jeder Seite bereit, damit Tastaturnutzer sich wiederholende Navigation überspringen können.
<!-- HTML -->
<a href="#main" class="skip-link">Skip to main content</a>
<nav>...</nav>
<main id="main">...</main>
/* CSS - Hidden until focused */
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
ARIA (Accessible Rich Internet Applications) fügt HTML-Elementen semantische Bedeutung hinzu. Es sollte jedoch vorsichtig verwendet werden:
role="presentation" oder aria-hidden="true" nicht bei fokussierbaren Elementen<button> ist immer besser als <div role="button">. ARIA fügt kein Verhalten hinzu - es ändert nur, wie assistive Technologien Elemente interpretieren. Wenn Sie umfangreiches ARIA verwenden, überdenken Sie Ihre HTML-Struktur.
Verwenden Sie diese, um barrierefreie Namen bereitzustellen, wenn visuelle Labels nicht ausreichen.
<!-- aria-label: Direct label --> <button aria-label="Close dialog"> <svg>...</svg> </button> <!-- aria-labelledby: Reference another element --> <div role="dialog" aria-labelledby="dialog-title"> <h2 id="dialog-title">Confirm Delete</h2> ... </div> <!-- Multiple labels --> <button aria-labelledby="btn-label icon-label"> <span id="btn-label">Download</span> <svg aria-hidden="true">...</svg> <span id="icon-label" class="sr-only">PDF file</span> </button>
Bietet zusätzliche Beschreibung oder Anweisungen.
<input type="text" id="username" aria-describedby="username-hint username-error"> <div id="username-hint">Must be 3-20 characters</div> <div id="username-error" hidden>Username already taken</div>
Kündigen dynamische Inhaltsänderungen für Screen Reader an.
<!-- Polite: Announce when screen reader is idle --> <div aria-live="polite" aria-atomic="true"> Items in cart: 3 </div> <!-- Assertive: Announce immediately (use sparingly) --> <div role="alert" aria-live="assertive"> Error: Payment failed </div> <!-- Status: For status updates --> <div role="status" aria-live="polite"> Loading... </div>
<div class="accordion">
<h3>
<button
aria-expanded="false"
aria-controls="panel1"
id="accordion1">
Section 1
</button>
</h3>
<div
id="panel1"
role="region"
aria-labelledby="accordion1"
hidden>
Content...
</div>
</div>
<div class="tabs">
<div role="tablist" aria-label="Products">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1">
Description
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1">
Reviews
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
Description content...
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
Reviews content...
</div>
</div>
<div role="dialog" aria-labelledby="dialog-title" aria-describedby="dialog-desc" aria-modal="true"> <h2 id="dialog-title">Confirm Action</h2> <p id="dialog-desc">Are you sure you want to delete this item?</p> <button>Cancel</button> <button>Delete</button> </div>
Ausreichender Farbkontrast stellt sicher, dass Text für Nutzer mit Sehschwäche oder Farbenblindheit lesbar ist.
WCAG 2.1 Level AA Kontrastanforderungen:
/* GOOD: Sufficient contrast */
.text {
color: #333333; /* Dark gray */
background: #FFFFFF; /* White */
/* Contrast ratio: 12.6:1 ✓ */
}
.button {
color: #FFFFFF; /* White */
background: #0066CC; /* Blue */
/* Contrast ratio: 6.6:1 ✓ */
}
/* BAD: Insufficient contrast */
.subtle-text {
color: #AAAAAA; /* Light gray */
background: #FFFFFF; /* White */
/* Contrast ratio: 2.3:1 ✗ FAILS */
}
.link {
color: #90CAF9; /* Light blue */
background: #FFFFFF; /* White */
/* Contrast ratio: 2.0:1 ✗ FAILS */
}
Farbe sollte nicht das einzige visuelle Mittel zur Informationsvermittlung sein. Fügen Sie Text, Muster, Icons oder andere Indikatoren hinzu.
<!-- BAD: Color only --> <span style="color: red;">Error</span> <span style="color: green;">Success</span> <!-- GOOD: Color + text/icon --> <span class="error"> <svg aria-hidden="true">⚠</svg> Error: Invalid input </span> <span class="success"> <svg aria-hidden="true">✓</svg> Success: Saved </span> <!-- Graph example --> <!-- BAD: Color-coded lines only --> <!-- GOOD: Color + patterns/labels --> <svg> <line stroke="blue" stroke-dasharray="5,5" /> <!-- Dashed --> <text>Category A</text> <line stroke="red" stroke-dasharray="10,5" /> <!-- Different dash --> <text>Category B</text> </svg>
Inhalte müssen lesbar und funktional sein, wenn Text auf bis zu 200% ohne assistive Technologie vergrößert wird.
rem, em, %) anstelle fester Pixel für Schriftgrößen<meta name="viewport" maximum-scale=1>/* GOOD: Relative units */
body {
font-size: 100%; /* 16px typically */
}
h1 {
font-size: 2.5rem; /* 40px */
}
p {
font-size: 1rem; /* 16px */
line-height: 1.5; /* 24px */
}
/* BAD: Fixed pixels */
body {
font-size: 16px;
}
h1 {
font-size: 40px;
}
prefers-reduced-motion/* Respect user motion preferences */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Touch target sizing */
button,
a {
min-width: 44px;
min-height: 44px;
padding: 12px;
}
Barrierefreiheitstests erfordern mehrere Ansätze. Kein einzelnes Tool erfasst alle Probleme.
Trennen Sie Ihre Maus und testen Sie Ihre Website nur mit der Tastatur:
Häufige Screen Reader:
Was zu testen ist: