Komponenten · ARIA-Techniken

Status, Fortschritt & Benachrichtigungen

Statusmeldungen sind Rückmeldungen, die etwas über Erfolg, Fortschritt, Wartezustand oder Fehler sagen, ohne den Fokus zu verschieben – und genau deshalb bekommen Screenreader sie ohne zusätzliche Auszeichnung gar nicht mit. Die Lösung sind Live-Regionen mit role="status", role="alert" oder role="progressbar", die schon vor der Meldung im DOM stehen und danach nur noch mit Text befüllt werden.

„Wird geladen“, „Schritt 3 von 5“, „Gespeichert“, „12 Treffer“ – für sehende Nutzer sind das beiläufige Details am Rand des Blickfelds. Wer mit einem Screenreader arbeitet, merkt von diesen Änderungen nichts, solange sie nicht ausdrücklich angekündigt werden: Der virtuelle Cursor steht irgendwo anders, und ein stiller DOM-Wechsel löst keine Ausgabe aus. Diese Seite baut auf den Grundlagen der Live-Regionen auf und wendet sie auf die drei Muster an, die in echten Projekten praktisch immer vorkommen: Fortschritt, Ladezustand und kurzlebige Benachrichtigungen.

Das Wichtigste in Kürze

  • WCAG 4.1.3 „Statusmeldungen“ (Stufe AA) verlangt, dass solche Meldungen über Rolle oder Eigenschaften programmatisch ermittelbar sind – ohne dass sie den Fokus bekommen.
  • Der Container muss vorher existieren. Ein Element, das gleichzeitig mit seinem Text ins DOM kommt, wird von vielen Screenreadern nicht angesagt.
  • role="status" ist der Standard (implizit höflich und ganzheitlich), role="alert" nur für Dringendes, role="progressbar" für messbaren Fortschritt.
  • <progress> ist die native Lösung – ohne value-Attribut wird der Balken automatisch unbestimmt.
  • aria-busy="true" markiert den Bereich, der gerade arbeitet; die eigentliche Ansage macht trotzdem die Live-Region.
  • Nicht doppeln: role="alert" bringt aria-live="assertive" schon mit. Beides zusammen führt bei manchen Screenreadern zur doppelten Ansage.

Was eine Statusmeldung ist – und was nicht

Das Erfolgskriterium 4.1.3 Statusmeldungen definiert sie als Inhaltsänderung, die keine Änderung des Kontexts ist und Auskunft gibt über den Erfolg oder das Ergebnis einer Aktion, über einen Wartezustand, über den Fortschritt eines Vorgangs oder über das Vorhandensein von Fehlern.

Diese Abgrenzung ist in der Praxis wichtiger, als sie klingt – sie entscheidet darüber, ob du überhaupt eine Live-Region brauchst.

Das sind Statusmeldungen:

  • „5 Ergebnisse gefunden“ nach einer Suche
  • „3 Artikel“ neben dem Warenkorb-Symbol
  • „Ungültige Eingabe“ über dem Feld für die Postleitzahl
  • ein Lade-Symbol, das erscheint, während im Hintergrund gearbeitet wird
  • ein Fortschrittsbalken, der während eines Uploads wächst
  • „Ihr Formular wurde erfolgreich gesendet“
  • „5 Fehler auf dieser Seite“ in einer Fehlerübersicht

Das sind ausdrücklich keine Statusmeldungen:

  • Dialoge, die den Fokus übernehmen. Ein modaler Dialog ist eine Kontextänderung; Screenreader kündigen ihn ohnehin an, weil der Fokus hineinwandert.
  • Aufklappende Bedienelemente wie Akkordeons, Tabs oder Menüs. Für die gilt 4.1.2 („Name, Rolle, Wert“) mit aria-expanded – siehe Rollen, States & Properties.
  • Die Trefferliste selbst. Nur die kurze Zusammenfassung („5 Ergebnisse“) ist eine Statusmeldung, nicht die 5 Ergebnisse.
  • Zusätzlich eingeblendete Formularfelder, die von einer Antwort abhängen – es sei denn, du kündigst sie ausdrücklich mit einem Statustext an.

Merksatz: Wenn der Fokus sich bewegt, brauchst du keine Live-Region. Wenn er stehen bleibt und sich trotzdem etwas Wichtiges ändert, brauchst du eine.

Die passende Rolle wählen

ARIA kennt mehrere Rollen, die automatisch Live-Regionen sind. Sie unterscheiden sich in Dringlichkeit und darin, ob der ganze Bereich oder nur das Geänderte vorgelesen wird:

Rolle implizit aria-live implizit aria-atomic Wofür
status polite true Ergebnisse, Bestätigungen, Zähler
alert assertive true Fehler, Warnungen, Sitzungsablauf
log polite false Chatverläufe, fortlaufende Protokolle
progressbar messbarer Fortschritt
timer off Countdown, Stoppuhr
marquee off Laufschriften, Ticker

In über 90 % der Fälle ist role="status" richtig. role="alert" unterbricht die laufende Ausgabe – das ist der Grund, warum es sparsam eingesetzt gehört: Wer bei jedem gespeicherten Feld unterbrochen wird, schaltet den Screenreader irgendwann leiser statt aufmerksamer zu werden.

aria-atomic="true" (bei status und alert schon eingebaut) sorgt dafür, dass der gesamte Inhalt der Region vorgelesen wird und nicht nur das eine geänderte Wort. Bei „Noch 2 Plätze frei“ willst du genau das – sonst hörst du bloß „2“.

Die goldene Regel: Der Container muss vorher da sein

Das ist der Fehler, an dem die meisten selbstgebauten Toasts scheitern, und er ist tückisch, weil das Markup im fertigen DOM völlig korrekt aussieht. Screenreader beobachten Live-Regionen, die sie kennen. Ein Element, das gleichzeitig mit seinem Text neu ins Dokument kommt, war zum Zeitpunkt der Änderung noch keine beobachtete Region – die Ansage fällt aus.

Die W3C-Technik ARIA22 formuliert das als Prüfschritt: Der Container mit role="status" muss vorhanden sein, bevor die Meldung erscheint.

<!-- FALSCH: Region und Text entstehen im selben Moment -->
<script>
  const el = document.createElement("p");
  el.setAttribute("role", "status");
  el.textContent = "12 Treffer";
  document.body.append(el); // wird oft nicht angesagt
</script>
<!-- RICHTIG: leere Region liegt von Anfang an im DOM -->
<p id="suchstatus" role="status" class="visually-hidden"></p>

<script>
  document.getElementById("suchstatus").textContent = "12 Treffer";
</script>
Zwei Zeitleisten im Vergleich. Oben der falsche Weg: Beim Laden der Seite existiert keine Live-Region, dann werden Region und Text gleichzeitig eingefügt, der Screenreader bleibt stumm. Unten der richtige Weg: Beim Laden liegt bereits eine leere Region mit role=status im DOM, später wird nur der Text eingesetzt, und der Screenreader sagt „12 Treffer“ an.
Derselbe Endzustand im DOM, zwei völlig verschiedene Ergebnisse: Nur die Region, die schon vor der Änderung existiert, wird zuverlässig angesagt.

Zwei verwandte Stolpersteine aus derselben Ecke:

  • Zweimal derselbe Text wird nicht wiederholt. Setzt du „Gespeichert“ ein zweites Mal in dieselbe Region, sieht der Screenreader keine Änderung und schweigt. Leere die Region kurz vorher (textContent = ""), oder formuliere die Meldung mit einem variablen Bestandteil („Gespeichert um 14:02“).
  • Meldungen direkt beim Seitenaufbau werden verschluckt. Was schon im HTML steht, wenn die Seite fertig geladen ist, gilt nicht als Änderung. Serverseitig gerenderte Erfolgsmeldungen brauchen deshalb einen anderen Weg – etwa eine Überschrift, die den Fokus bekommt.

Fortschritt anzeigen

Das native <progress>

Für einen bestimmbaren Fortschritt gibt es ein eigenes HTML-Element. Es bringt die Rolle progressbar und die Wertansage von Haus aus mit:

<label for="upload">Upload</label>
<progress id="upload" value="60" max="100">60 %</progress>

Drei Details, die oft falsch gemacht werden:

  • max ist standardmäßig 1, nicht 100. Ohne max="100" bedeutet value="60" also 6000 %.
  • Ein min-Attribut gibt es nicht. Der Minimalwert ist immer 0. Wer eine Skala mit anderem Startpunkt braucht (Speicherplatz, Temperatur), nimmt <meter>.
  • Der Text zwischen den Tags ist kein Label. „60 %“ ist nur ein Fallback für sehr alte Browser. Die Beschriftung kommt über <label> oder aria-label.

Unbestimmter Fortschritt

Lässt du das value-Attribut weg, wird der Balken automatisch unbestimmt – der Browser zeigt die typische wandernde Animation, und du kannst ihn per :indeterminate gestalten:

<progress id="import" max="100" aria-label="Import läuft"></progress>

Von unbestimmt auf bestimmt wechselst du, indem du value setzt; zurück geht es mit element.removeAttribute("value").

Eigenbau mit role="progressbar"

Wenn <progress> gestalterisch nicht reicht – etwa für einen Ring oder einen mehrstufigen Balken – übernimmst du die Semantik per ARIA:

<div
  role="progressbar"
  aria-valuenow="60"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuetext="60 Prozent, etwa 40 Sekunden verbleibend"
  aria-label="Upload"
>
  <span class="bar" style="inline-size: 60%"></span>
</div>

aria-valuetext ist der unterschätzte Teil: Ohne dieses Attribut hört man „60“ – mit ihm eine Formulierung, die tatsächlich weiterhilft. Wichtig ist außerdem, dass aria-valuenow synchron zum sichtbaren Balken aktualisiert wird. Ein Balken, der optisch wächst, während die Ansage bei 0 stehen bleibt, ist schlechter als gar keiner, weil er falsche Sicherheit erzeugt.

Randnotiz – bitte nicht jede Prozentzahl ansagen. Ein Fortschrittsbalken, der sich hundertmal pro Sekunde aktualisiert, sollte nicht hundertmal pro Sekunde sprechen. Screenreader gehen damit unterschiedlich um; verlässlicher ist es, den Balken selbst nicht als Live-Region zu behandeln und stattdessen an sinnvollen Wegmarken – Start, 50 %, Abschluss – eine separate role="status"-Meldung zu schicken. Bei sehr kurzen Vorgängen (unter etwa einer Sekunde) reicht die Abschlussmeldung völlig.

Ladezustände: aria-busy plus Statusmeldung

Wenn die Dauer unbekannt ist – der klassische Spinner –, gibt es keinen Wert, den man melden könnte. Hier arbeitest du mit zwei Zutaten: aria-busy markiert den Bereich, der gerade neu befüllt wird, und eine höfliche Statusmeldung sagt es an.

<section aria-busy="true" aria-describedby="ladehinweis">
  <p id="ladehinweis" role="status">Ergebnisse werden geladen …</p>
  <!-- Liste wird ersetzt -->
</section>

Nach dem Laden setzt du aria-busy="false" und ersetzt den Text durch das Ergebnis: „12 Treffer“. Der Unterschied zur reinen Spinner-Grafik ist gewaltig – aus einer stummen Animation wird eine nachvollziehbare Rückmeldung.

aria-busy hat noch einen zweiten Zweck: Baust du eine Liste in mehreren Schritten auf, unterdrückt aria-busy="true" auf dem Container die Ansage jeder einzelnen Zwischenänderung. Erst wenn du auf false schaltest, wird der Endzustand gemeldet. Das ist der saubere Weg für „Mehr laden“-Muster.

Toasts und kurzlebige Benachrichtigungen

Ein Toast ist eine Meldung, die kurz eingeblendet wird und von selbst wieder verschwindet. Damit sie überhaupt ankommt, ist der Toast-Container eine Live-Region, die dauerhaft im DOM liegt und nur befüllt wird:

<!-- liegt dauerhaft im Dokument, auch wenn nichts angezeigt wird -->
<div id="toast-bereich" role="status" aria-live="polite"></div>
Drei Karten nebeneinander zeigen die Statusmuster. Links ein Fortschrittsbalken bei 60 Prozent mit dem Hinweis progress-Element und der Screenreader-Ansage „Upload, 60 Prozent“. In der Mitte ein Ladezustand mit Spinner, aria-busy gleich true und der Ansage „Ergebnisse werden geladen“. Rechts ein grüner Toast mit Häkchen und dem Text „Änderungen gespeichert“ samt Schließen-Knopf und der Ansage „Änderungen gespeichert“.
Die drei Muster im Vergleich: bestimmter Fortschritt, unbestimmter Ladezustand und Toast – jeweils mit dem, was ein Screenreader daraus macht.

Wie lange muss ein Toast stehen bleiben?

Ein Toast, der nach zwei Sekunden verschwindet, ist für langsames Lesen, starke Vergrößerung oder Screenreader-Nutzung schlicht zu schnell weg. Das ist kein Geschmacksthema: 2.2.1 „Zeitbegrenzungen anpassbar“ (Stufe A) behandelt automatisch ablaufende Einblendungen als Zeitbegrenzung. Die ARIA-Authoring-Practices raten deshalb generell davon ab, Meldungen automatisch verschwinden zu lassen.

Praktische Leitplanken, die sich bewährt haben:

  • Faustregel für die Standzeit: etwa fünf Sekunden plus Lesezeit – und immer mit Schließen-Knopf.
  • Beim Überfahren mit der Maus oder bei Tastaturfokus im Toast: Timer anhalten.
  • Für alles, was der Nutzer später noch braucht (Referenznummern, Fehlerdetails), ist ein Toast der falsche Ort. Solche Informationen gehören dauerhaft auf die Seite.

Der Doppelansage-Fallstrick

role="alert" bringt aria-live="assertive" bereits mit. Setzt du beides gleichzeitig und ergänzt vielleicht noch aria-atomic="true", lesen manche Screenreader – vor allem VoiceOver unter iOS – die Meldung zweimal vor. Entscheide dich für einen Weg: entweder die Rolle oder die aria-live-Attribute.

<!-- reicht vollkommen -->
<div role="alert">Sitzung läuft in 2 Minuten ab.</div>

So testest du Statusmeldungen

Automatische Prüfwerkzeuge finden hier fast nichts – ob eine Ansage tatsächlich kommt, zeigt nur der echte Test:

  1. Screenreader an, Augen zu. Löse die Aktion aus und achte darauf, ob überhaupt etwas gesagt wird. Wie das praktisch geht, steht bei Mit NVDA testen und Mit VoiceOver testen.
  2. Zweimal dieselbe Aktion auslösen. Kommt die Meldung beim zweiten Mal auch? Wenn nicht, ist es das Problem mit dem unveränderten Text.
  3. Den Sprachverlauf prüfen. NVDA bietet dafür den Sprachbetrachter, bei VoiceOver hilft das Beschriftungsfenster – so siehst du schwarz auf weiß, was angesagt wurde.
  4. Mit Tastatur allein bedienen. Bleibt der Fokus, wo er war? Wenn eine Meldung den Fokus klaut, ist es keine Statusmeldung mehr, sondern eine Kontextänderung.

Häufige Fehler

  • Live-Region wird zusammen mit der Meldung eingefügt – die häufigste Ursache für stumme Toasts.
  • Spinner ohne Textansage – „lädt“ bleibt für Screenreader unsichtbar.
  • aria-valuenow nicht aktualisiert – der Balken steht in der Ansage still.
  • max bei <progress> vergessen – der Standardwert ist 1, nicht 100.
  • role="alert" plus aria-live="assertive" – doppelte Ansage.
  • Alles assertive – ständige Unterbrechungen statt höflicher Hinweise.
  • Toast als einzige Quelle für eine wichtige Bestätigung oder Referenznummer.
  • Toast zu kurz sichtbar ohne Möglichkeit, ihn wiederzubekommen.
  • Eine Region für alles – Fehler, Erfolg und Fortschritt in denselben Container zu schreiben führt zu abgeschnittenen und vermischten Ansagen.

Häufige Fragen

<progress> oder role="progressbar"?

Nimm das native <progress>, wann immer es gestalterisch reicht – es bringt Rolle und Wertansage mit und funktioniert auch dann, wenn dein JavaScript scheitert. role="progressbar" ist für Eigenbauten gedacht, bei denen das native Element nicht zum Design passt. Das entspricht der ersten Regel von ARIA.

Warum wird meine Statusmeldung nicht vorgelesen?

In den allermeisten Fällen, weil der Container erst zusammen mit dem Text ins DOM kommt. Zweithäufigste Ursache: Der Text ist identisch mit dem vorherigen. Dritthäufigste: Die Region liegt in einem Element mit display: none – versteckte Live-Regionen sagen nichts an. Nutze stattdessen eine visually-hidden-Klasse, die den Text visuell entfernt, aber im Accessibility-Baum lässt.

Ist ein Toast ohne Schließen-Knopf ein WCAG-Verstoß?

Wenn er automatisch verschwindet und die Information nicht anderswo verfügbar ist: ziemlich sicher ja, über 2.2.1. Enthält der Toast nur eine redundante Bestätigung, die auch sichtbar auf der Seite steht, ist die Lage entspannter. Der Schließen-Knopf kostet wenig und löst die Frage – nimm ihn einfach.

Wie sage ich Formularfehler an?

Nicht über einen Toast. Fehler gehören direkt ans Feld und zusätzlich in eine Fehlerübersicht, die den Fokus bekommt – das erklärt Fehlermeldungen barrierefrei im Detail. Eine role="alert"-Zusammenfassung („3 Felder brauchen noch Ihre Aufmerksamkeit“) ist eine sinnvolle Ergänzung, kein Ersatz.

Braucht aria-busy zwingend eine Live-Region?

Ja. aria-busy allein sagt nichts an – es unterdrückt lediglich Zwischenansagen und markiert den Bereich als „in Arbeit“. Die eigentliche Meldung kommt aus der Live-Region.

Fazit

Status muss hörbar sein, und zwar ohne dass der Fokus springt: bestimmter Fortschritt über das native <progress> (mit max!) beziehungsweise role="progressbar" samt aria-valuetext, unbestimmtes Laden über aria-busy plus einer höflichen role="status"-Meldung, Toasts als dauerhaft vorhandene Live-Region mit genügend Standzeit und Schließen-Knopf.

Die eine Regel, die alle anderen trägt: Der Container ist vor der Meldung da. Wer das beherzigt, sparsam ankündigt, Werte synchron hält und für kritische Informationen nie allein auf eine flüchtige Einblendung setzt, erfüllt 4.1.3 nicht nur formal, sondern baut eine Oberfläche, die auch ohne Blick auf den Bildschirm nachvollziehbar bleibt.

Quellen

Gratis E-Book PDF Das Praxishandbuch

Kostenloses E-Book

Das Praxishandbuch für sauberes, zugängliches Web

Alles rund um semantisches HTML, Barrierefreiheit, WCAG & BFSG, GEO und SEO — praxisnah und am echten Code. In mehreren Feedbackschleifen von Leserinnen und Lesern verbessert.

  • 3.000+ Downloads
  • 7. Auflage
  • 37 Seiten
  • PDF

Kein Spam. Abmeldung jederzeit mit einem Klick möglich.