Barrierefreie Komponenten · Interaktive Widgets

Dialoge & Modals barrierefrei

Ein modaler Dialog unterbricht alles andere: Er legt sich über die Seite, verlangt eine Entscheidung und sperrt den Hintergrund, bis er geschlossen ist. Genau diese Sonderstellung macht ihn barrierefrei anspruchsvoll. Ein Overlay, das nur optisch „darüber“ liegt, aber den Fokus im Hintergrund lässt, ist für Tastatur- und Screenreader-Nutzung eine Falle – man bedient unsichtbare Elemente hinter einer Wand.

Vier Dinge muss ein Modal richtig machen: den Fokus hineinbewegen, ihn drin halten, Esc zum Schließen anbieten und beim Schließen den Fokus zurückgeben. Das klingt nach Arbeit – und war es früher auch. Heute nimmt einem das native <dialog>-Element den größten Teil ab.

Der moderne Weg: <dialog> + showModal()

Das <dialog>-Element mit der Methode showModal() ist meine Standardlösung. Es liefert Fokus-Management, Esc-Handling, einen Backdrop und einen inerten (nicht bedienbaren) Hintergrund frei Haus:

<button type="button" id="open">Einstellungen öffnen</button>

<dialog id="dlg" aria-labelledby="dlg-titel">
  <h2 id="dlg-titel">Einstellungen</h2>
  <p>…</p>
  <button type="button" id="close" autofocus>Schließen</button>
</dialog>
const dlg = document.getElementById('dlg');
document.getElementById('open').addEventListener('click', () => dlg.showModal());
document.getElementById('close').addEventListener('click', () => dlg.close());

Was showModal() automatisch erledigt:

  • Fokus hinein: Er landet auf dem Element mit autofocus, sonst auf dem ersten fokussierbaren Element im Dialog.
  • Fokus gefangen: Tab zirkuliert innerhalb des Dialogs, der Hintergrund ist inert.
  • Esc schließt: ohne eigenen Code.
  • Fokus zurück: Beim Schließen kehrt der Fokus zu dem Element zurück, das den Dialog geöffnet hat – also zum Button.

Dass eine einzige Methode all das mitbringt, war für mich einer der Gründe, alte Eigenbau-Modals nach und nach abzulösen. Wichtig ist nur: Es muss showModal() sein, nicht show(). Letzteres öffnet den Dialog nicht-modal – ohne Fokusfalle und ohne inerten Hintergrund.

Worauf es trotzdem ankommt

Auch mit <dialog> bleiben ein paar Dinge in deiner Hand:

  • Eine Überschrift, die den Dialog benennt. Per aria-labelledby auf die <h2> verweisen, damit der Screenreader beim Öffnen ansagt, worum es geht.
  • Eine sichtbare Schließen-Möglichkeit. Esc allein genügt nicht – ein klar erreichbarer „Schließen“-Button gehört dazu.
  • Sinnvoller Startfokus. autofocus auf das Element setzen, mit dem man am ehesten weitermacht. Bei reinen Bestätigungen oft der unkritische Button, nicht „Löschen“.

Den zugänglichen Namen des Dialogs liefert übrigens dasselbe Prinzip wie sonst auch – mehr zu aria-labelledby und Co. unter Rollen, States & Properties.

Wenn <dialog> nicht reicht

Müssen ältere Umgebungen bedient werden, baut man das Verhalten manuell nach: ein Container mit role="dialog" und aria-modal="true", eine selbstgeschriebene Fokusfalle, Esc-Behandlung, der Hintergrund per inert oder aria-hidden="true" deaktiviert, und beim Schließen der Fokus zurück auf den Auslöser.

<div role="dialog" aria-modal="true" aria-labelledby="dlg-titel">
  <h2 id="dlg-titel">Einstellungen</h2>

</div>

Das ist deutlich mehr Code und mehr Fehlerpotenzial – weshalb ich es nur dort mache, wo das native Element wirklich nicht in Frage kommt. Das passt zur ersten Regel von ARIA: so wenig selbst nachbauen wie möglich.

Brauchst du überhaupt ein Modal?

Ein modaler Dialog ist ein kräftiges Mittel – es zwingt zur Unterbrechung. Für unkritische Hinweise ist das oft zu viel. Newsletter-Overlays, die sich nach drei Sekunden über den Inhalt legen, sind das Paradebeispiel für ein Modal, das niemand wollte. Ich setze Modals sparsam ein: für echte Entscheidungen und gefährliche Aktionen, nicht für Beiläufiges.

Häufige Fehler

  • show() statt showModal(). Keine Fokusfalle, kein inerter Hintergrund – also gar nicht wirklich modal.
  • Fokus bleibt im Hintergrund. Tab erreicht Elemente hinter dem Overlay, die optisch verdeckt sind.
  • Kein Rücksprung des Fokus. Nach dem Schließen landet der Fokus am Seitenanfang statt beim Auslöser.
  • Kein Esc, kein sichtbares Schließen. Mindestens einer der beiden Wege muss da sein – besser beide.
  • Fehlender Name. Ohne aria-labelledby/aria-label weiß ein Screenreader nicht, was der Dialog ist.

Häufige Fragen

Trapt das native <dialog> den Fokus wirklich zuverlässig?

Bei showModal() ja – der Hintergrund wird inert, Tab bleibt im Dialog. Diesen Effekt bei einer Eigenbau-Lösung sauber hinzubekommen, ist erfahrungsgemäß der schwierigste Teil; ein Grund mehr für das native Element.

role="dialog" oder role="alertdialog"?

alertdialog ist für dringende Unterbrechungen gedacht, die eine Antwort erfordern – etwa „Ungespeicherte Änderungen verwerfen?“. Für die meisten Dialoge ist dialog richtig.

Wie verhält sich ein Modal mit der Tastatur?

Fokus hinein beim Öffnen, gefangen während der Anzeige, Esc schließt, Fokus zurück zum Auslöser. Die allgemeinen Grundlagen dazu stehen unter Tastaturbedienung & sichtbarer Fokus.

Fazit

Ein barrierefreies Modal bewegt den Fokus hinein, hält ihn drin, schließt mit Esc und gibt den Fokus zurück. Das native <dialog> mit showModal() liefert genau das mit minimalem Code – ergänzt um eine benennende Überschrift und einen sichtbaren Schließen-Button. Und die ehrlichste Maßnahme bleibt: ein Modal nur dort einsetzen, wo die Unterbrechung wirklich gerechtfertigt ist.