Komponenten · Interaktive Widgets
Slider & Karussells barrierefrei
Ein barrierefreies Karussell hat einen Pause-Button vor den Folien im DOM, hält verdeckte Folien aus der Tabreihenfolge und stoppt die Rotation, sobald ein Element darin Fokus bekommt oder die Maus darüber steht. Rotiert es automatisch, bleibt der Folienbereich als Live-Region abgeschaltet – sonst wird die Ansage zur Dauerbeschallung.
Ich fange mit einer Meinung an: Das barrierefreieste Karussell ist oft gar keins. Automatisch rotierende Bilderslider im Seitenkopf sehen in Mockups gut aus, doch meist wird nur die erste Folie wahrgenommen, der Rest zieht ungesehen vorbei. Bevor ich ein Karussell baue, frage ich deshalb, ob eine schlichte Reihe von Inhalten denselben Zweck nicht besser erfüllt.
Wenn es aber ein Slider sein soll, dann richtig. Vier Themen entscheiden: Bewegung kontrollierbar machen, mit Tastatur und Zeiger bedienbar sein, die Struktur ansagbar machen und Bewegungsempfindlichkeit respektieren.
Das Wichtigste in Kürze
- 2.2.2 Pausieren, Beenden, Ausblenden (Stufe A) greift, wenn Bewegung automatisch startet, länger als fünf Sekunden läuft und parallel zu anderem Inhalt steht. Dann braucht es Pausieren, Stoppen oder Ausblenden.
- Der Pause-Button steht im DOM vor den Folien – auch wenn er optisch woanders sitzt. So findet ihn die Tastatur, bevor sie in den Inhalt läuft.
-
Der Container trägt
aria-roledescription="Karussell"plus Namen, jede Folierole="group"mitaria-roledescription="Folie"und einem Namen wie „2 von 4“. -
Bei Auto-Rotation ist der Folienbereich
aria-live="off". Erst wenn die Rotation steht, istpolitesinnvoll. - Die Rotation hält an, sobald ein Element im Karussell Fokus erhält – und ebenso bei Maus darüber.
- Verdeckte Folien sind nicht fokussierbar (
hiddenoderinert), sonst tabbt man in unsichtbare Links. -
prefers-reduced-motionrespektieren: Bei aktivierter Reduktion startet die Auto-Rotation nicht. - Nur-Wisch-Bedienung genügt nicht. Seit 2.5.7 braucht jede Ziehbewegung einen Klick-Weg – die Vor-/Zurück-Buttons sind er.
Bewegung muss kontrollierbar sein
Der Wortlaut von 2.2.2 ist präziser, als er meist zitiert wird. Drei Bedingungen müssen zusammenkommen, damit die Anforderung greift: Die Bewegung startet automatisch, sie dauert länger als fünf Sekunden, und sie steht parallel zu anderem Inhalt. Ein Karussell im Seitenkopf erfüllt alle drei – also braucht es einen klar erreichbaren Pause-Button, und zwar einen echten <button>:
<section
role="region"
aria-roledescription="Karussell"
aria-label="Aktuelle Angebote"
>
<!-- Steuerung zuerst im DOM: die Bremse liegt vor dem Inhalt -->
<button type="button" class="karussell-pause" aria-pressed="false">
Automatischen Wechsel pausieren
</button>
<div class="folien" aria-live="off" aria-atomic="false">
<div role="group" aria-roledescription="Folie" aria-label="1 von 4">…</div>
<div role="group" aria-roledescription="Folie" aria-label="2 von 4" hidden>…</div>
</div>
</section>
Zwei Details, die den Unterschied machen:
Der Button steht vor den Folien. Optisch darf er unter oder neben dem Karussell liegen – im Quelltext gehört er davor. Wer per Tastatur ankommt, soll die Bremse finden, bevor die Folien unter ihm weiterwandern. Das ist eine Empfehlung aus dem APG-Muster und in der Praxis der Punkt, den fast alle Bibliotheken falsch machen.
Der Zustand wandert mit. Pausiert heißt aria-pressed="true" und eine geänderte Beschriftung – nicht nur ein anderes Symbol. Wer statt aria-pressed zwei Buttons nimmt (Pause / Wiedergabe) und den jeweils passenden ausblendet, muss den Fokus mit umziehen, sonst verliert er ihn beim Umschalten.
Bewegung, die sich nicht anhalten lässt, ist nicht nur ein WCAG-Verstoß – sie kann Menschen mit Aufmerksamkeits- oder Gleichgewichtsstörungen aktiv stören. Der Pause-Button ist deshalb für mich keine Option, sondern Pflicht.
Struktur ansagbar machen
Ein Karussell ist für einen Screenreader ohne zusätzliche Auszeichnung eine Ansammlung von Bereichen, die kommen und gehen. Das APG-Muster gibt ihm einen Namen und eine Rollenbeschreibung:
-
Der Container ist eine
region(odergroup) mitaria-roledescription="Karussell"und einem Namen. Der Screenreader sagt dann „Aktuelle Angebote, Karussell“ statt „Bereich“. -
Jede Folie ist eine
groupmitaria-roledescription="Folie"und einem Namen, der die Position nennt: „2 von 4“ oder besser „2 von 4: Neu, Prüfbericht“. - Die Punkt-Navigation besteht aus Buttons, deren Name die Nummer enthält („Folie 3 von 4“). Der aktive Punkt bekommt
aria-currentoderaria-disabled– und einen sichtbaren Formunterschied, nicht nur eine andere Farbe (1.4.1).
Zur Live-Region eine Einordnung, die oft fehlt: Der Folienbereich bekommt bei automatischer Rotation aria-live="off". Sonst wird jeder Wechsel angesagt, während der Nutzer etwas anderes liest – und das im Sekundenrhythmus. Erst wenn die Rotation steht (weil sie pausiert wurde oder nie lief), macht aria-live="polite" Sinn: Dann ist jeder Wechsel eine Nutzeraktion, und die Ansage bestätigt sie. Mehr zum Mechanismus unter Live-Regionen.
Bewegungsempfindlichkeit respektieren
Manche Menschen reagieren auf Animationen mit Schwindel oder Übelkeit. Das Betriebssystem kennt dafür eine Einstellung, die der Browser als prefers-reduced-motion weitergibt. Ein Karussell sollte sie ernst nehmen und bei aktivierter Reduktion nicht von selbst loslaufen:
@media (prefers-reduced-motion: reduce) {
.karussell {
scroll-behavior: auto;
}
.karussell * {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
const reduziert = window.matchMedia('(prefers-reduced-motion: reduce)');
if (!reduziert.matches) startAutoplay();
// Auch reagieren, wenn die Einstellung während der Sitzung wechselt
reduziert.addEventListener('change', (e) => (e.matches ? stopAutoplay() : startAutoplay()));
Mein Standard ist: Auto-Rotation startet nur, wenn keine Reduktion gewünscht ist – und selbst dann mit Pause-Button. Animation ist eine Einladung, keine Zwangsmaßnahme.
Das ist streng genommen keine WCAG-AA-Pflicht, sondern gute Praxis – aber eine, die fast nichts kostet und für die betroffene Gruppe den Unterschied zwischen „nutzbar“ und „Seite verlassen“ macht.
Tastatur, Zeiger und Fokus
Auch der Rest muss ohne Maus funktionieren:
- Vor / Zurück sind echte Buttons, mit der Tastatur erreichbar und auslösbar – und mit mindestens 24 × 24 Pixel Trefferfläche. Pfeile als
<div>mit Klick-Handler sind der Klassiker unter den Fehlern. - Verdeckte Folien sind nicht fokussierbar. Links oder Buttons auf ausgeblendeten Slides gehören per
hiddenoderinertaus der Tab-Reihenfolge – sonst tabbt man ins Leere und der Fokus verschwindet aus dem Bild (2.4.7). - Kein Auto-Wechsel bei Fokus oder Hover im Slider. Das APG-Muster verlangt beides: Die Rotation stoppt, sobald ein Element im Karussell Tastaturfokus bekommt, und ebenso, solange die Maus darüber steht. Wer gerade liest oder bedient, soll nicht von der nächsten Folie überrascht werden.
- Wischen darf nicht der einzige Weg sein. Ein Karussell, das man nur per Swipe weiterschiebt, verstößt gegen 2.5.7 – die Vor-/Zurück-Buttons sind die geforderte Alternative und dürfen deshalb nicht nur bei Hover erscheinen.
- Pfeiltasten sind optional. Wer sie einbaut, muss das erwartete Modell vollständig liefern; für Vor/Zurück genügen Tab und Enter. Nur bei der Tab-Variante (
role="tablist") sind Pfeiltasten Pflicht – dann gilt das Muster von Tabs.
Worauf es bei Fokusreihenfolge und Sichtbarkeit grundsätzlich ankommt, steht unter Tastaturbedienung & sichtbarer Fokus.
Die einfachere Bauweise: Scroll-Snap
Es gibt eine Variante, die viel von dem, was oben aufwendig ist, gratis mitbringt: ein waagerechter Scroll-Container mit CSS Scroll Snap. Der Browser liefert dann Tastatur-Scrollen, Touch-Wischen, Scrollbalken und Trägheit – und alle Folien liegen gleichzeitig im DOM, sichtbar und erreichbar.
.slider {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
gap: 1rem;
}
.slider > * {
flex: 0 0 min(100%, 40rem);
scroll-snap-align: start;
}
Der Container braucht tabindex="0" und einen Namen, damit er per Tastatur scrollbar und ansagbar ist – wie bei einer breiten Tabelle. Die Vor-/Zurück-Buttons rufen dann nur scrollBy() auf. Kein hidden, kein inert, kein Fokus-Management: Da würd ich immer zuerst prüfen, ob diese Bauweise reicht, bevor ich ein Folien-Karussell mit Ein- und Ausblenden baue.
Der Preis: kein Auto-Ablauf mit Überblendung, und die Punkt-Navigation muss die Scroll-Position auslesen statt einen Index zu verwalten.
Performance nicht vergessen
Karussells laden gern viele große Bilder auf einmal – das schadet der Ladezeit doppelt, weil das Karussell meist im ersten Bildschirm steht und damit direkt den LCP betrifft. Drei Handgriffe:
- Nur die erste Folie eifrig laden, alle weiteren mit
loading="lazy". widthundheightan jedem Bild, damit nichts springt.fetchpriority="high"am Bild der ersten Folie, wenn es das LCP-Element ist.
Das Thema gehört eng zu den Core Web Vitals; ein schwergewichtiges Karussell ist überraschend oft die Ursache schlechter Werte.
So testest du es
- Fünf Sekunden warten. Läuft es automatisch weiter? Dann muss ein Pause-Button sichtbar und erreichbar sein.
- Tab drücken. Landet der Fokus zuerst auf der Pause, oder erst nach den Folien?
- Fokus in eine Folie setzen. Bleibt die Rotation stehen?
- Weitertabben. Landet der Fokus auf Elementen verdeckter Folien?
- Reduzierte Bewegung einschalten (macOS: Systemeinstellungen → Bedienungshilfen → Anzeige; Windows: Einstellungen → Barrierefreiheit → Visuelle Effekte). Startet die Rotation trotzdem?
- Nur klicken, nicht wischen. Kommt man mobil per Tippen zur nächsten Folie?
Häufige Fehler
- Auto-Rotation ohne Pause. Verstößt gegen 2.2.2 und stört.
- Pause-Button hinter den Folien im DOM. Die Tastatur findet ihn zuletzt.
-
aria-live="polite"bei Auto-Rotation. Jeder Wechsel wird angesagt, dauerhaft. -
prefers-reduced-motionignoriert. Animation läuft, obwohl das System Reduktion signalisiert. - Verdeckte Folien bleiben fokussierbar. Tab landet auf unsichtbaren Elementen.
-
Pfeile als
<div>. Ohne echten Button keine Tastaturbedienung. - Wechsel bei Hover oder Fokus. Reißt Nutzende aus dem, was sie gerade tun.
- Punkt-Navigation nur farblich unterschieden und ohne Nummer im Namen.
- Nur Swipe auf Touch-Geräten – Verstoß gegen 2.5.7.
Mit oder ohne Auto-Play – im Komponenten-Baukasten sind beide Varianten bedienbar, samt Pause-Schalter, Live-Region-Verhalten und Testprotokoll.
Häufige Fragen
Sind Karussells grundsätzlich nicht barrierefrei?
Doch, sie lassen sich zugänglich bauen – es ist nur aufwendig, alles richtig zu machen. Meine Empfehlung bleibt trotzdem, vorher zu prüfen, ob ein statisches Layout den Zweck nicht einfacher erfüllt. Der Aufwand ist dann nicht gespart, sondern in Inhalt umgewandelt.
Reicht ein Pause-Button, oder muss man auch stoppen können?
Pausieren genügt der WCAG-Anforderung – das Kriterium nennt Pausieren, Stoppen und Ausblenden als Alternativen, nicht als Summe. Wichtig ist, dass der Mechanismus sichtbar, erreichbar und eindeutig beschriftet ist und dass der Zustand programmatisch ablesbar bleibt.
Wie kennzeichne ich die aktive Folie in der Punkt-Navigation?
Die Punkte sind Buttons; der aktive bekommt aria-current="true" und einen Namen mit Nummer („Folie 3 von 4“). Verlasse dich nicht allein auf die Farbe – ein Größen- oder Formunterschied hilft allen. Manche Umsetzungen nehmen stattdessen aria-disabled="true" für den aktiven Punkt; beides ist vertretbar.
Muss die Rotation auch bei Maus-Hover stoppen?
Das APG-Muster sagt ja, und es ist die richtige Entscheidung: Wer mit der Maus auf eine Folie zeigt, will sie meist lesen oder anklicken. Wenn sie unter dem Zeiger wegwandert, klickt man auf die falsche.
Was ist mit Karussells, die nur bei Nutzerklick wechseln?
Dann greift 2.2.2 nicht, weil die Bewegung nicht automatisch startet. Alles andere bleibt: Buttons statt Divs, verdeckte Folien aus der Tabreihenfolge, Struktur ansagbar, und eine höfliche Live-Region ist hier tatsächlich sinnvoll.
Fazit
Ein barrierefreies Karussell lässt sich pausieren, hält verdeckte Folien aus der Fokusreihenfolge, stoppt bei Fokus und Hover, startet bei prefers-reduced-motion nicht von selbst und ist auch ohne Wischen bedienbar. Die Struktur macht aria-roledescription ansagbar, die Bremse steht im Quelltext vor den Folien.
Das ist machbar – aber genug Aufwand, dass sich die Eingangsfrage lohnt: Braucht es das Karussell überhaupt, oder tut es eine ruhige Reihe von Inhalten nicht besser? Und wenn ja: Ein Scroll-Snap-Container nimmt einem drei der sechs Baustellen ab, bevor man die erste Zeile JavaScript schreibt.
Quellen
- Carousel Pattern (W3C ARIA APG – aria-roledescription, Rotationssteuerung, aria-live-Verhalten, Stopp bei Fokus und Hover)
- Understanding SC 2.2.2 Pause, Stop, Hide (W3C – die drei Bedingungen: automatischer Start, mehr als fünf Sekunden, parallel zu anderem Inhalt)