Ressourcen · Nachschlagen
Snippet-Bibliothek
Diese Sammlung enthält die zwölf Muster, die in fast jedem Projekt gebraucht werden – vom Skip-Link über die visuell versteckte Beschriftung bis zur Live-Region. Jedes Snippet steht hier so kurz wie möglich, mit dem Stolperstein, an dem es beim Kopieren am häufigsten kaputtgeht.
Ein Snippet ist ein Startpunkt, keine Garantie. Gerade bei ARIA gilt: blind kopieren ist genau der Weg, auf dem schlechte Auszeichnung in die Welt kommt. Ich empfehle deshalb, jedes Muster einmal zu verstehen, bevor es ins eigene Projekt wandert – die ausführliche Begründung steht jeweils im verlinkten Artikel.
Das Wichtigste in Kürze
- Neun von zwölf Snippets brauchen kein JavaScript und kein einziges ARIA-Attribut. Das ist Absicht: Nativ ist robuster.
-
.visually-hiddengehört in jedes Projekt.display: noneundvisibility: hiddensind kein Ersatz – sie nehmen den Text auch dem Screenreader. - Der Skip-Link muss der erste fokussierbare Link im DOM sein und beim Fokus sichtbar werden. Ein dauerhaft versteckter Skip-Link hilft niemandem.
- Icon-Buttons brauchen einen Namen im Text, nicht nur ein
title-Attribut. Das SVG bekommtaria-hidden="true"undfocusable="false". -
<details>und<dialog>bringen Tastaturbedienung und Zustand von Haus aus mit – inklusive Esc, Fokus-Falle und Backdrop beim modalen Dialog. - Live-Regionen müssen vor dem Einfügen des Textes im DOM stehen. Ein Container, der zusammen mit seiner Meldung erzeugt wird, bleibt stumm.
- Fokus nie ohne Ersatz entfernen.
outline: noneohne eigenen sichtbaren Stil verstößt gegen WCAG 2.4.7.
Native HTML zuerst. Die meisten Snippets hier kommen ohne JavaScript und mit so wenig ARIA wie möglich aus. Warum das die robustere Wahl ist, steht unter Die erste Regel von ARIA.
Nur-für-Screenreader-Klasse (.visually-hidden)
Diese Utility-Klasse blendet Text visuell aus, lässt ihn aber für Screenreader lesbar – ideal für zusätzliche Beschriftungen, Sprungmarken-Titel oder Statusmeldungen.
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
}
Warum so umständlich? Jede der Deklarationen hat einen Grund: Die Größe von einem Pixel verhindert, dass Browser das Element als leer wegoptimieren; clip-path schneidet den sichtbaren Inhalt weg, ohne ihn aus dem Accessibility Tree zu entfernen; white-space: nowrap verhindert, dass langer Text im Ein-Pixel-Kasten umbricht und dadurch wieder Platz beansprucht.
Stolperstein: Enthält der versteckte Bereich einen Link oder Button, entsteht ein unsichtbarer Tabstopp. Für solche Fälle gibt es die Variante .visually-hidden-focusable, die das Element bei Fokus wieder einblendet – genau das tut auch der Skip-Link unten.
Inhalt überspringen (Skip-Link)
Der erste fokussierbare Link der Seite, normalerweise unsichtbar, bei Tastaturfokus sichtbar. Er springt direkt zum Hauptinhalt und erspart Tastatur-Nutzenden, sich durch die komplette Navigation zu tabben. Diese Website nutzt genau dieses Muster.
<a class="skip-link" href="#inhalt">Zum Inhalt springen</a>
<!-- Header / Navigation -->
<main id="inhalt" tabindex="-1">…</main>
.skip-link {
position: absolute;
top: -3rem;
left: 0.5rem;
padding: 0.5rem 1rem;
background: #fff;
color: #000;
transition: top 0.15s ease;
}
.skip-link:focus {
top: 0.5rem;
}
Warum das tabindex="-1" am <main>? Ohne es setzen manche Browser den Fokus nach dem Sprung nicht auf das Ziel, sondern lassen ihn am Link – der nächste Tab landet dann wieder in der Navigation. Mit dem Attribut wird <main> programmatisch fokussierbar, ohne in die Tab-Reihenfolge zu geraten.
Stolperstein: Ein Skip-Link, der nur per opacity: 0 versteckt ist, bleibt anklickbar und erzeugt Verwirrung; einer mit display: none ist gar nicht erreichbar. Details unter Skip-Links.
Icon-Button mit zugänglichem Namen
Ein Button, der nur ein Icon zeigt, braucht trotzdem einen Namen. Das SVG bekommt aria-hidden, der Name kommt als visuell verstecktes <span>.
<button type="button" class="icon-button">
<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false">
<path d="M3 6h18M3 12h18M3 18h18" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" />
</svg>
<span class="visually-hidden">Menü öffnen</span>
</button>
Der Name im Text ist einem aria-label vorzuziehen: Er wird von Übersetzungswerkzeugen erfasst, ist im DOM sichtbar und lässt sich mit Sprachsteuerung ansprechen. focusable="false" am SVG verhindert einen zusätzlichen Tabstopp in älteren Edge-Versionen.
Stolperstein: Ändert der Button seinen Zustand – etwa von „Menü öffnen“ zu „Menü schließen“ –, muss der Text mitwandern, nicht nur das Icon. Mehr unter Icons & SVGs und Buttons vs. Links.
Sichtbarer Fokus mit :focus-visible
Der Fokusrahmen ist die Anzeige, wo man sich befindet – ihn zu entfernen, ist der häufigste Verstoß gegen die Tastaturbedienbarkeit. Mit :focus-visible bekommt man einen deutlichen Rahmen für Tastatur-Nutzung, ohne bei Mausklicks einen Rahmen zu zeigen.
:focus-visible {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
border-radius: 2px;
}
Stolperstein: outline: none an einer allgemeinen Regel wie *:focus löscht den Rahmen überall – auch dort, wo kein Ersatz definiert ist. Wenn eine Komponente einen eigenen Fokus-Stil braucht, dann lokal und mit sichtbarem Ergebnis.
Akkordeon ohne JavaScript (<details>)
Für einfaches Auf- und Zuklappen braucht es kein Skript und kein ARIA – <details> und <summary> bringen Tastaturbedienung und Zustand von Haus aus mit.
<details>
<summary>Versand & Lieferzeit</summary>
<p>Wir liefern innerhalb von 2–4 Werktagen innerhalb Deutschlands.</p>
</details>
Stolperstein: Ein <summary>, das eine Überschrift enthalten soll, braucht die Überschrift innerhalb – <summary><h3>Titel</h3></summary> – und nicht darüber. Und Inhalte in einem geschlossenen <details> sind für die Seitensuche im Browser unsichtbar; bei wichtigen Inhalten ist das ein Argument dagegen. Wann es reicht und wann nicht: Akkordeons.
Dialog mit <dialog>
Das native <dialog> mit showModal() bringt Fokus-Falle, Schließen per Esc und einen Backdrop von selbst mit – Dinge, die man sonst mühsam nachbaut. Das <form method="dialog"> schließt den Dialog beim Absenden.
<button type="button" data-dialog-open="hinweis">Hinweis öffnen</button>
<dialog id="hinweis" aria-labelledby="hinweis-titel">
<form method="dialog">
<h2 id="hinweis-titel">Cookies</h2>
<p>Diese Seite nutzt nur technisch notwendige Cookies.</p>
<button>Verstanden</button>
</form>
</dialog>
document.querySelectorAll('[data-dialog-open]').forEach((btn) => {
btn.addEventListener('click', () => {
document.getElementById(btn.dataset.dialogOpen).showModal();
});
});
Stolperstein: show() statt showModal() erzeugt einen nicht-modalen Dialog ohne Fokus-Falle und ohne Esc – ein häufiger Fehler beim Kopieren. Und nach dem Schließen muss der Fokus zurück auf das auslösende Element; das macht der Browser bei showModal() in aktuellen Versionen, aber nicht in jedem Szenario. Hintergrund: Dialoge / Modals.
Eingabefeld mit Label und Fehlermeldung
Jedes Feld bekommt ein echtes <label for>. Hilfetext und Fehlermeldung werden über aria-describedby verknüpft – beide IDs, durch Leerzeichen getrennt. aria-invalid setzt man erst, wenn ein Fehler vorliegt.
<label for="email">E-Mail-Adresse</label>
<input
id="email"
name="email"
type="email"
autocomplete="email"
required
aria-describedby="email-hilfe email-fehler"
aria-invalid="true"
/>
<p id="email-hilfe">Wir nutzen die Adresse nur für die Bestellbestätigung.</p>
<p id="email-fehler" class="fehler">
Bitte gib eine gültige E-Mail-Adresse ein (z. B. name@beispiel.de).
</p>
Stolperstein: Wird die Fehlermeldung erst im Fehlerfall ins DOM geschrieben, verweist aria-describedby bis dahin auf eine nicht existierende ID. Das ist unschön, aber harmlos – kritisch wird es umgekehrt: Steht aria-invalid="true" von Anfang an, ist die Angabe bedeutungslos. Mehr unter Labels & Beschriftungen und Validierung & Pflichtfelder.
Fehlerliste über dem Formular
Das zuverlässigste Muster für Formularfehler: eine Zusammenfassung mit Sprunglinks, die nach dem Absenden den Fokus bekommt.
<div id="fehlerliste" role="alert" tabindex="-1" hidden>
<h2>Zwei Angaben fehlen noch</h2>
<ul>
<li><a href="#email">E-Mail-Adresse: Bitte gib eine Adresse mit @ ein.</a></li>
<li><a href="#plz">Postleitzahl: Bitte fünf Ziffern eingeben.</a></li>
</ul>
</div>
Ein Fokuswechsel auf diesen Container ist verlässlicher als jede Live-Region, weil die Ansage nicht vom Timing abhängt. Details unter Fehlermeldungen barrierefrei.
Breadcrumb-Navigation
Eine Brotkrume ist eine geordnete Liste in einem benannten <nav>. Die aktuelle Seite ist kein Link, sondern Text mit aria-current="page".
<nav aria-label="Sie sind hier">
<ol>
<li><a href="/index.html">Start</a></li>
<li><a href="/ressourcen.html">Ressourcen</a></li>
<li><span aria-current="page">Snippet-Bibliothek</span></li>
</ol>
</nav>
Stolperstein: Trennzeichen als Textzeichen im Markup werden mitgelesen – „Start Schrägstrich Ressourcen“. Sie gehören ins CSS. Warum <ol> und warum aria-current: Breadcrumbs.
Statusmeldung (Live-Region)
Wenn sich nach einer Aktion etwas ändert, ohne dass der Fokus springt – „gespeichert“, „3 Treffer“ –, gehört die Meldung in eine Live-Region.
<p id="status" role="status" aria-live="polite" class="visually-hidden"></p>
document.getElementById('status').textContent = 'Formular gespeichert.';
Stolperstein: Der Container muss schon im DOM stehen, bevor Text hineinkommt – wird er zusammen mit der Meldung erzeugt, bleibt es still. Und derselbe Text zweimal hintereinander wird nicht erneut angesagt; in solchen Fällen hilft ein kurzes Leeren des Containers. Wann polite, wann assertive: Live-Regionen.
Sprachwechsel im Text
Wenn mitten im deutschen Text ein englischer Begriff steht, den die Sprachausgabe falsch ausspricht, hilft ein lang-Attribut am umgebenden Element.
<p>Das Muster heißt <span lang="en">progressive enhancement</span>.</p>
Stolperstein: Das gilt für ganze Phrasen, nicht für Fachwörter, die im Deutschen etabliert sind – „Browser“ oder „Link“ brauchen kein lang. Und das <html lang="de"> am Dokumentanfang ist die Voraussetzung dafür, dass der Wechsel überhaupt eine Wirkung hat.
Datentabelle mit Beschriftung
Eine Tabelle braucht eine <caption> und echte Kopfzellen mit scope – dann kann ein Screenreader jede Zelle mit ihrer Zeilen- und Spaltenüberschrift ansagen.
<table>
<caption>Öffnungszeiten der Beratungsstelle</caption>
<thead>
<tr><th scope="col">Tag</th><th scope="col">Uhrzeit</th></tr>
</thead>
<tbody>
<tr><th scope="row">Montag</th><td>9–16 Uhr</td></tr>
<tr><th scope="row">Dienstag</th><td>9–13 Uhr</td></tr>
</tbody>
</table>
Stolperstein: Tabellen für Layout sind seit Jahren tabu, tauchen aber in E-Mail-Templates und alten CMS-Bausteinen weiter auf. Für Layout gibt es Grid und Flexbox.
Bewegung reduzieren
Animationen können Schwindel und Übelkeit auslösen. Wer die Systemeinstellung respektiert, erfüllt WCAG 2.3.3 mit drei Zeilen.
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Stolperstein: Ganz auf 0 zu setzen bringt Skripte durcheinander, die auf das Ende einer Animation warten – daher der Wert von 0,01 Millisekunden statt null.
Wann ein Snippet nicht reicht
Drei Muster habe ich hier bewusst nicht abgekürzt, weil eine Kopiervorlage sie nicht trägt: Tabs, Comboboxen und Karussells. Sie brauchen Zustandsverwaltung, Tastaturmuster mit Pfeiltasten und eine Entscheidung darüber, was beim Wechsel angesagt wird – das lässt sich nicht in zehn Zeilen unterbringen, ohne etwas Falsches zu behaupten.
Wenn du eines dieser Muster brauchst, ist der Weg über den jeweiligen Artikel kürzer als über ein Snippet, das du danach reparieren musst. Und ganz allgemein: Bevor ich ein ARIA-Muster nachbaue, schau ich immer erst, ob HTML es schon kann. In den letzten Projekten war die Antwort öfter „ja“, als ich beim Anfangen gedacht hätt.
Häufige Fehler beim Kopieren
-
aria-labelan einem<div>. Ohne Rolle wird der Name meist ignoriert. Namen brauchen ein Element, das eine Rolle hat. -
role="button"an einem<div>, ohnetabindex="0"und ohne Tastatur-Handler. Dann nimm einfach<button>. - Doppelte IDs aus mehrfach eingefügten Snippets – zerstört jede
for- undaria-describedby-Verknüpfung. -
aria-hidden="true"an einem fokussierbaren Element. Es verschwindet für Screenreader, bleibt aber per Tab erreichbar – ein Widerspruch, den Prüfwerkzeuge zuverlässig melden. - Placeholder statt Label. Verschwindet beim Tippen und hat oft zu wenig Kontrast.
Häufige Fragen
Darf ich diese Snippets in kommerziellen Projekten verwenden?
Ja. Es sind Standardmuster aus HTML, CSS und minimalem ARIA – daran gibt es nichts zu lizenzieren. Was du mitnehmen solltest, ist nicht der Code, sondern die Begründung dahinter.
Warum steht hier so wenig ARIA?
Weil ARIA nichts hinzufügt, was HTML nicht schon kann, sondern nur Informationen für assistive Technologien ergänzt. Falsch gesetzt macht es Elemente unbenutzbar. Ein <button> ist zuverlässiger als jedes role="button".
Ersetzt eine Komponentenbibliothek diese Snippets?
Teilweise. Gute Bibliotheken bringen getestete Muster mit – aber sie treffen Entscheidungen, die du kennen solltest, etwa welchen Namen ein Icon-Button bekommt oder ob ein Dialog modal ist. Die Snippets hier sind der Maßstab, an dem man eine Bibliothek prüft.
Wie teste ich, ob ein kopiertes Snippet funktioniert?
Mit drei Durchläufen: einmal nur mit der Tastatur, einmal mit 200 % Zoom, einmal mit einem Screenreader über den betroffenen Bereich. Das dauert wenige Minuten und findet mehr als jedes automatische Werkzeug – siehe Selbst testen.
Vom Snippet zur Komponente
Jedes Snippet gehört zu einem ausführlichen Artikel – und dort warten auch die Muster, die für eine Kopiervorlage zu groß sind:
| Snippet / Thema | Der passende Artikel |
|---|---|
| Skip-Link | Skip-Links |
| Icon-Button | Icons & SVGs |
<details>-Akkordeon |
Akkordeons |
<dialog> |
Dialoge & Modals |
| Feld + Fehlermeldung | Fehlermeldungen |
| Fehlerliste, Pflichtfelder | Validierung & Pflichtfelder |
| Breadcrumbs | Breadcrumbs |
| Live-Region | Status & Benachrichtigungen |
| Datentabelle | Komplexe Datentabellen |
| Datumseingabe | Datepicker |
| Tabs, Comboboxen, Karussells | Tabs · Comboboxen · Slider & Karussells |
Für die schweren Muster gibt es außerdem ein eigenes Werkzeug: Diese Bibliothek ist fertig zum Kopieren, der Komponenten-Baukasten ist konfigurierbar. Er stellt die Varianten nebeneinander, die hier absichtlich fehlen – natives <dialog> gegen ARIA-Nachbau, <details> gegen Button mit aria-expanded, Tabs mit automatischer gegen manuelle Aktivierung – jeweils mit Vorschau, Code und einem Testprotokoll für Tastaturweg und Screenreader-Ausgabe. Kurz: Bibliothek, wenn die Frage „wie schreibt man das?“ lautet. Baukasten, wenn sie „welche Variante nehme ich?“ lautet.
Fazit
Zwölf Muster, die den größten Teil des Alltags abdecken – und in neun Fällen ohne JavaScript auskommen. Wer lieber zuerst das Warum versteht, folgt den Links in die barrierefreien Komponenten; welches WCAG-Kriterium hinter einem Muster steht, verrät die Kriterien-Referenz. Zum schnellen Nachschlagen passen außerdem die Cheat-Sheets & Checklisten, das Glossar und die kommentierte Tool-Sammlung.
Quellen
- ARIA Authoring Practices Guide – Patterns (W3C WAI – referenzierte Tastaturmuster)
- Das dialog-Element (MDN –
showModal(), Fokusverhalten,method="dialog")