A presentation at A-Tag 2025 in in Vienna, Austria by Manuel Matuzovic
Tooltips, Toggletips und Dialoge A-Tag, 15. Mai 25
accessibility-cookbook.com matuzo.social htmhell.dev matuzo.at manuel@matuzo.at
Hallo! Mein Name ist Manuel Matuzovic. Ich bin Frontend Developer, Berater, Lehrer und Auditor aus Wien und lebe aktuell in Graz. Ich freue mich sehr wieder hier sein zu dürfen als Besucher, aber, auch mittlerweile zum dritten Mal, als Vortragender. Der A-Tag ist ein sehr wichtiges Event für mich. Ich war 2016 zum ersten Mal hier und hatte überhaupt keine Ahnung von Barrierefreiheit und Barrierefreiheit im Web und…
…acht Jahre später habe ich ein Buch über Barrierefreiheit bei O’Reilly veröffentlicht. Diese Event hat einen sehr großen Beitrag dazu geleistet und auch die involvierten Personen, die ich natürlich im Buch in der Danksagung erwähnt habe. Ein weiterer Grund, warum dieses Event so wichtig ist, und dass es auch wieder in dieser Form auftritt, ist, dass wir Wissensvermittlung und Networking in der Barrierefreiheit mehr denn je benötigen. Das hat mit dem European Accessibility Act zu tun aber auch mit der Tatsache, dass das Web einfach immer noch in einem fürchterlichen Zustand ist. In Vorbereitung auf diesen Vortrag habe ich Beispielseiten mit bestimmten Mustern gesucht, egal ob barrierefrei oder nicht, und ich habe 20 Sites besucht. Auf 10 von diesen 20 Sites habe ich gefunden, wonach ich gesucht habe, leider war nur neun von zehn der Lösungen nicht barrierefrei, aber das ist nicht das Thema von heute.
accessibility-cookbook.com
Das eigentliche Thema ist Tooltips, Toggletips und Dialoge. Ich habe dieses Thema bewusst gewählt, weil sich da in letzter Zeit sehr viel getan hat und weil manche Lösungen in HTML und JavaScript vielleicht auch ein bisschen verwirrend sein könnten. Und anscheinend habe ich unterbewusst dieses Thema gewählt, weil wir o ensichtlich unfähig sind barrierefreie Tooltips und Dialoge zu machen.
Ich möchte mit Tooltips anfangen.
Ein Tooltip ist ein kontextbezogenes Pop-up das eine Beschreibung für ein Element anzeigt. Es tritt in Form einer Blase auf und die Idee ist, dass es zusätzlichen Kontext zu vielleicht missverständlichen Elementen bietet. Es wird gern verwendet, wenn man einfach nur ein Piktogramm hat ohne Text, was an sich eine schlechte Idee ist, um optional eine Erklärung für das Element zu bieten. Ein Tooltip zeigt nur Text an, keinen Richtext oder interaktive Elemente und es ist standardmäßig nicht sichtbar und wird nur bei Hover und Fokus angezeigt. Es schließt sich wieder sobald man sich mit der Maus wegbewegt vom Tooltip selber oder auch von dem Element, das es getriggert hat oder wenn man Escape drückt.
Ein kontextbezogenes Popup, das eine Beschreibung für ein Element anzeigt. • kleine Informationsblasen oder -kästchen, die den Zweck von ansonsten missverständlichen Elementen verdeutlichen • Wird meist nach einer kurzen Verzögerung (1 Sekunde+) gezeigt • :hover/:focus → kurzen Verzögerung → anzeigen • mouseout/blur/Escape → nicht mehr anzeigen • Nur Text, kein Richtext oder interaktive Elemente
Wenn man ein Tooltip erstellt ist es wichtig, dass es geschlossen werden kann ohne dass man die Maus oder Fokus bewegt, beispielsweise mit Escape. Man sollte den Zeiger über die Tooltip-Bubble bewegen können. Die Tooltip-Blase bleibt sichtbar eben bis Hover oder Fokus entfernt wird, die Nutzer:in das Element schließt oder die Information nicht mehr gültig ist.
• Schließen ohne Mauszeiger oder Fokus zu bewegen (bspw. mit Escape) • Mauszeiger kann über die Tooltip-Bubble bewegt werden, ohne dass sie verschwindet • Die Tooltip-Bubble bleibt sichtbar bis Hover- oder Fokus entfernt wird, die Nutzer:in das Element schließt oder die Informationen nicht mehr gültig sind • WCAG 1.4.13 (AA)
Hier ist ein Beispiel auf on.orf.at. Wenn man ihm Video Player über die Piktogramme hovert sieht man wofür sie stehen. Also zum Beispiel “Ton aus” oder “Einstellungen” oder “Vollbild”. Wenn man die Tastatur verwendet, bekommt man die selbe Information.
Jetzt schauen wir uns kurz ein paar gängige Fehler an. Ein Klassiker ist, dass Buttons, die die Information anzeigen sollen, nicht fokussierbar sind.
• Button ist nicht fokussierbar
Auf einer Website der Uniqa-Versicherung sieht man Info-Buttons. Wenn ich mit der Maus drüber hovere, bekomme ich ein Tooltip. Wenn ich die Tastatur verwende, werden sie einfach übersprungen.
wohnen.uniqa.at
Ähnliches haben wir beim Mobilfunkanbieter Spusu. Man sieht eine Liste der Tarife und Smartphones und pro Paket gibt es einen Info-Button und zwei normale Buttons und es sind zig Elemente auf dieser Seite. Wenn ich die Maus verwende und wieder über so einen Info-Button hovere bekomme ich ein Tooltip. Wenn ich das Keyboard verwende, wird alles übersprungen, weil hier anscheinend kein einziger semantischer Button richtig verwendet wird.
spusu.at/spusu12000
Ein weiterer Fehler ist, dass Tooltips oft nur bei hover, aber nicht bei Focus sichtbar sind.
• Button ist nicht fokussierbar • Nur bei :hover sichtbar, aber nicht :focus
Auf magenta.at sieht man in der Kopfzeile einige Piktogramme. Bei hover sieht man wofür sie stehen, zum Beispiel “Warenkorb” oder “Kundenportal”. Wenn ich die Tastatur verwende, kann ich die Links und Buttons zwar fokussieren, aber die Tooltips werden nicht sichtbar.
magenta.at/internet/internet-zuhause
Ein weiterer Fehler ist, dass die Tolltip-Bubble verschwindet, wenn man die Maus bewegt.
• Button ist nicht fokussierbar • Nur bei :hover sichtbar, aber nicht :focus • Bubble verschwindet wenn man die Maus bewegt
Hier bin ich auf der Webseite der Generali Versicherung. Neben dem Login mit ID Austria gibt es wieder einen Info-Button. Der ist nur mit der Maus zugänglich und wenn ich mit der Maus über den Button hovere und versuche den Text zu markieren oder kopieren, dann schließt sich die Tooltip automatisch.
meine.generali.at/iss/gui/kundenportal/login/login.seam?conversationId=309878
Der letzte gängige Fehler ist, dass es keine Verknüpfung zwischen dem Link oder Button, der die Anzeige steuert, und dem Ausgabeelement gibt.
• Button ist nicht fokussierbar • Nur bei :hover sichtbar, aber nicht :focus • Bubble verschwindet wenn man die Maus bewegt • Keine semantische Verknüpfung mit dem Trigger
Oft sind es einfach zwei separate Elemente. Ein Button und ein div.
<button aria-label=”Detail”> <svg>…</svg> </button> <div class=”tooltip”> </div>
Tooltips kann man nativ in HTML umsetzen.
Dafür kann man das Title-Attribut nehmen. Wenn der Button nicht gelabelt ist, wird der Wert des title-Attributs als Accessible Name verwendet.
<button title=”Einstellungen”> <svg>…</svg> </button>
Wenn der Button schon einen Accessible Name hat, verwendet man das Attribut für zusätzliche Information. Wenn man ein Tolltip braucht, rate ich davon ab, dass title-Attribut zu verwenden.
<button title=”Video-Player verwalten”> <span class=”vh”>Einstellungen</span> <svg>…</svg> </button>
Das hat mehrere Gründe. 1. ist es nur für Maus-User verfügbar, nicht für Keyboard- oder Touch User. Für Screen User unter Umständen.
• Nur verfügbar für Maus (und Screen Reader User)
Hier ist ein Zitat von Steve Faulkner: “If you want to hide content from mobile and tablet users as well as assistive tech users and keyboard only users, use the title attribute.”
“If you want to hide content from mobile and tablet users as well as assistive tech users and keyboard only users, use the title attribute.” Steve Faulkner
Weitere Gründe sind, dass es unter Umständen nicht gezoomt werden kann, das Styling kann nicht angepasst werden und das Standardstyling ist grundsätzlich eher suboptimal und es erfüllt nicht die WCAG Kriterien nicht, wobei es, soweit ich weiß, eh ausgenommen ist.
• Nur verfügbar für Maus (und Screen Reader User) • Kann unter Umständen nicht gezoomt werden • Styling kann nicht angepasst werden • Erfüllt nicht WCAG-Kriterien (Muss auch nicht)
Ein Tooltip kann man auch selber machen mit der Hilfe von ARIA.
Wenn der Inhalt im Tooltip den Button labeln soll, kann man role=“tooltip” auf die Information legen und den Button durch den Tooltip-Inhalt via aria-labelledby=“tp1” labeln.
<button aria-labelledby=”tp1”> <svg>…</svg> </button> <div id=”tp1” role=”tooltip”>Einstellungen</div>
Wenn der Inhalt im Tooltip unterstützend zu einem bestehenden Label ist, verwendet man statt aria-labelledby aria-describedby. Mit Tooltips sollte man grundsätzlich sparsam einsetzen. Wenn das User Interface ohne Tooltip nicht eindeutig ist, sollte das User Interface verbessert werden. Außerdem ist es selten eine gute Idee, Inhalte automatisch bei Hover oder Fokus anzuzeigen, da dass die Nutzung und Übersicht deutliche mühsamer machen kann.
<button aria-describedby=”tp1”> <span class=”vh”>Einstellungen</span> <svg>…</svg> </button> <div id=”tp1” role=”tooltip”>Video-Player verwalten</div>
Wenn es aber der Inhalt oder das User Interface nicht erlaubt, sind Toggletips eine gut Alternative für Tooltips.
Toggletips sind sehr ähnlich. Auch wieder kleine kontextbezogene Informations-Bubbles. Der große Unterschied zu Tooltips ist, dass Toggletips sich nicht automatisch öffnen sondern über Klick und Enter geöffnet werden. Schließen kann man sie auch wieder mit Klick und Enter und üblicherweise mit der Escape Taste oder wenn man daneben klickt.
Ein kontextbezogenes Popup, das eine Beschreibung für ein Element anzeigt. • kleine Informationsblasen oder -kästchen, die den Zweck von ansonsten missverständlichen Elementen verdeutlichen • Nur Text, kein Richtext oder interaktive Elemente • Klick/Enter → anzeigen • Klick/Enter/Esc/blur/click outside → nicht mehr anzeigen
Theoretisch könnte man das details-Element dafür verwenden, aber man muss den Inhalt auch steuern und stylen können.
<details> <summary> Info </summary> Hier gibt es ein paar wichtige Details für dich. </details>Das geht mit dem mit :details-content Pseudoelement. Aktuell kann das Firefox aber noch nicht.
details[open]::details-content { … }
Das Wichtige bei der Umsetzung mit Aria ist, dass wir die beiden Attribute aria-describedby und aria-labelledby nicht mehr verwenden können, da die Information ja nicht nur visuell erst bei Aktivierung des Buttons verfügbar sein soll sondern auch programmatisch
Stattdessen kann man eine Live Region verwenden. Der Button hat aria-expanded=“false”.
<button data-tooltip=”Hier steht Detailinfo…” aria-expanded=”false”> <span class=”vh”>Info</span> <svg>…</svg> </button> <div role=”status”></div>
Bei Klick wird aria-expanded=“true” gesetzt und die Live Region mit dem Text gefüllt.
<button data-tooltip=”Hier steht Detailinfo…” aria-expanded=”true”> <span class=”vh”>Info</span> <svg>…</svg> </button> <div role=”status”>Hier steht Detailinfo…</div>
Wenn noch mal auf dem Button geklickt wird, wird die Live Region wieder geleert.
<button data-tooltip=”Hier steht Detailinfo…” aria-expanded=”false”> <span class=”vh”>Info</span> <svg>…</svg> </button> <div role=”status”></div>
Auch hier gibt es einige gängige Fehler:
• Button ist nicht fokussierbar
Im Anmeldeformular von a1.net gibt es pro Eingabefeld einen Login-Button. Mit der Maus kann man den klicken und schließen. Mit dem Keyboard erreicht man den Button nicht.
Außerdem haben Buttons oft keine Text-Alternative oder kommunizieren auch nicht ob das kontrollierte Element offen ist.
• Button ist nicht fokussierbar • Button hat keinen Accessible Name • Button kommuniziert Zustand nicht
Auf fitinn.at gibt es eine Auflistung der Abos. Bei den Leistungen gibt es jeweils einen Info-Button. Der ist fokussierbar aber es gibt keine Textalternative und es ist auch nicht klar, ob die Information gerade angezeigt wird oder nicht.
fitinn.at/mitgliedschaft
Außerdem wird die Information oft nur visuell wiedergegeben.
• Button ist nicht fokussierbar • Button hat keinen Accessible Name • Button kommuniziert Zustand nicht • Information wird nicht angekündigt
Wenn man mehr als nur flachen Text darstellen möchte, können sich Dialoge eignen
Zusätzliches Fenster zum Hauptfenster.
• Beinhaltet üblicherweise viel Inhalt und interaktive Elemente • Modal oder Nicht-Modal • Klick/Enter → anzeigen • Klick/Enter/Esc/Click Outside → nicht mehr anzeigen Dialoge beinhalten üblicherweise mehr Text als Tooltips oder Toggletips und auch interaktive Elemente. Es gibt sie in einer modalen oder in einer nicht modalen Ausführung. ff Sie werden entweder programmatisch geö net oder bei Klick auf einen Button.
• Cookie-Banner • Flyout-Navigationen • Wichtige Hinweise • Login, Nachricht erstellen und andere eingebettete Aktionen
Auf der Social Media Plattform Mastodon öffnet sich beispielsweise ein Fenster wenn man absichtlich oder unabsichtlich versucht eine bestehenden Nachricht zu überschreiben.
lutz.at hat eine sehr umfangreiche Navigation Struktur. Das Menü öffnet sich in einem eigenen Dialog Fenster.
Man kann Dialoge selber in ARIA umsetzen, aber das ist mit recht viel Aufwand verbunden.
Man benötigt ein Element mit der role=dialog, man muss einen Namen vergeben, man muss es Modal machen, vielleicht auch fokussierbar machen, Fokus managen und man muss sicherstellen, dass die Elemente im Hintergrund nicht zugänglich sind.
<div role=”dialog” aria-labelledby=”heading” aria-modal=”true” tabindex=”-1”> <button>Schließen</button> <h1 id=”heading”> Login </h1> </div>In HTML ist das alles viel einfacher, weil wir das dialog-Element haben.
Man fügt das Element hinzu im HTML und benennt es, zum Beispiel mit aria-labelledby.
<button>Login</button>
<dialog aria-labelledby=”heading”> <form method=”dialog”> <button>Schließen</button> </form> <h1 id=”heading”> Login </h1> </dialog>In JavaScript kann man den Dialog dann mit der showModal() Methode öffnen.
<script> document.querySelector(‘button’).addEventListener(‘click’, e => { document.querySelector(‘dialog’).showModal() }) </script>Den Dialog kann man via JavaSkript schließen, bei Click auf einem Button in einem Formular mit method=”dialog” oder mit der Escape Taste. In Chrome und Edge kann man das close closedby vergeben und es auch so ermöglichen, dass man außerhalb vom Dialog klicken kann, um das Fenster zuschließen.
<dialog aria-labelledby=”heading” closedby=”any”> <form method=”dialog”> <button>Schließen</button> </form> <h1 id=”heading”> Login </h1> </dialog>In Chrome und Edge kann man Dialoge außerdem komplett ohne JavaScript öffnen und schließen. Das geht mit invokers-commands. Mit dem command-Attribut definiert man welche Aktion ausgeführt werden soll, zum Beispiel show-modal.
<button command=”show-modal”> Login </button>
Zudem muss man mit dem commandfor-Attribut festlegen, welches Element gesteuert wird.
<button command=”show-modal” commandfor=”my-modal” > Login </button>
Das selbe Prinzip kann man dann auch noch auf den Schließen-Button anwenden mit der Aktion “close”. Dann schauen wir uns noch die nicht-modalen Fenster an.
<dialog aria-labelledby=”heading” id=”my-modal” closedby=”any”> <button command=”close” commandfor=”my-modal”>close</button> <h1 id=”heading”>Login</h1> </dialog>Das können auch Cookie Banner sein, Teilen-Widgets, Chat-Widgets, Lern- oder Tutorial-Fenster, Videos oder auch komplexe Menüs.
Der Cookie-Banner auf ikea.at ist beispielsweise nicht Modal.
Auf w24.at gibt es ein Fenster mit dem Livestream des Sender.
Auf digitales.wien.gv.at ist ein Chatbot eingebaut.
Auf on.orf.at gibt es Fenster, die Teile des Interfaces erklären.
• Button ist nicht fokussierbar Auch hier wieder ein gängiger Fehler: Der Button ist nicht fokussierbar.
Auf bipa.at gibt es eine Galerie. Man kann Bilder klicken, um sie größer anzusehen aber man kann sich nicht fokussieren und mit dem Keyboard größer machen.
bipa.at
• Button ist nicht fokussierbar • Fokus wird nicht gemanagt Wird auch Fokus nicht gemanaged.
Auf intersport.at kann man sich die Verfügbarkeit eines Produktes in den Shops anzeigen lassen. Dabei wieder ein neues Fenster geöffnet, nur leider bleibt der Fokus im Hintergrund.
intersport.at
Auf Kärnten.at öffnet sich ein modales Fenster bei Klick und Fokus wird auch richtig reinbewegt aber beim Schließen kommt der Fokus nicht mehr zurück zu dem Button, der das Fenster geöffnet hat. Der Fokus landet irgendwo auf der Seite.
Weitere Fehler sind das modale Fenster oft nicht modal sind. D.h. dass Inhalt im Hintergrund immer noch zugänglich ist. Manchmal, zum Glück selten, kommt es vor, dass modale Fenster nicht richtig versteckt werden, nur visuell sichtbar sind, aber aufgrund ihrer Modalität den Rest des Inhalts blockieren.
• Button ist nicht fokussierbar • Fokus wird nicht gemanagt • Keine oder falsche Rolle für den Dialog • Dialog hat keinen Accessible Name • Modale Dialog sind nicht modal • Modale Dialog werden nicht richtig versteckt
Zum Abschluss möchte jetzt noch kurz über das Popover Attribut sprechen, weil es die Erstellung von Toggletips und nicht-modalen Dialogen vereinfacht.
Um ein Popover zu erstellen legt man das popover-Attribut auf das zu steuernde Element und verknüpft es mit dem Button über das popovertarget-Attribut auf dem Button. Und das war’s auch schon. popover versteckt das Element. Und die Verknüpfung über popovertarget ö net und schließt das Element und schaltet das ariaexpanded Attribut auf dem Button implizit um. ff Das Popover Attribut selber liefert keine Semantik, nur Funktionalität. Dementsprechend muss man dem popover eine Rolle geben, zum Beispiel dialog, menu, list oder tooltip.
<button popovertarget=”tooltip1”> Einstellungen </button> <span popover id=”tooltip1” role=”tooltip”> E-Mail und Passwort ändern, etc. </span>
Ein Popover ist ein nicht-modales, schwebendes UI-Teil mit ergänzendem oder kontextbezogenem Inhalt. Es kann üblicherweise ein Dialog, Tooltip, Menu oder Listbox sei. Popover schweben üblicherweise nicht irgendwo im Hauptfenster, sonst sind an dem Trigger-Button ausgerichtet.
Vorrübergehender Inhalt. • ein schwebendes UI-Element mit ergänzendem oder kontextbezogenem Inhalt • Nicht-Modal • Kann üblicherweise ein Dialog, Tooltip, Menu oder Listbox sein • Oft an dem Trigger-Button ausgerichtet • Klick/Enter → anzeigen • Klick/Enter/Esc/Click Outside → nicht mehr anzeigen
Beispiele dafür sind Content-Picker, wie Date- oder Emoji-Picker, Vorschläge für Formularelemente wie Autocomplete oder Action Menüs.
Hier ein Emoji-Picker.
Oder eine Combobox. Als Beispiele dienen auch alle Nicht-Modale Fenster, die ich bereits gezeigt habe.
Popover haben einen impliziten aria-expanded state.
In dem Screenshot sieht man, dass der Button expanded ist, obwohl kein aria-expanded Attribut explizit eingegeben worden ist.
Eigenschaft wenn [popover] nicht direkt auf den Button folgt. Wenn zwischen dem Button und dem Popover andere DOM Elemente sind bekommt der Button ein implizites aria-details Attribut.
• Impliziter aria-expanded state • Implizite aria-details
Zwischen dem Button und dem Popover befindet sich jetzt ein Absatz. Deswegen bekommt der Button ein implizites aria-details Attribut.
Wenn das popover ein generisches Element ist und man ihm keine Rolle vergibt, ist sie die Rolle implizit “group” und nicht “generic”.
• Impliziter aria-expanded state • Implizite aria-details Eigenschaft wenn [popover] nicht direkt auf den Button folgt. • Default Rolle: group
Man sieht den Chrome DevTools, dass das Popover-Span die Rolle “group” hat.
• Impliziter aria-expanded state • Implizite aria-details Eigenschaft wenn [popover] nicht direkt auf den Button folgt. • Default Rolle: group • Funktioniert deklarativ nur auf <button>s Deklarativ funktioniert popover (aktuell) nur auf <button>s.
Bei einem Popover bewegt sich der Fokus nicht automatisch in das Popover. Wenn man aber ein Element im Popover fokussiert und Escape drückt bewegt sich der Fokus zurück zum Button.
• Fokus bewegt sich nicht automatisch rein (außer bei <dialog> oder [autofocus]) • Fokus bewegt sich automatisch raus
Wenn man zwei Popover hat, eines öffnet und dann weiter fokussiert zum nächsten Button wird das Popover nicht automatisch geschlossen.
• Fokus bewegt sich nicht automatisch rein (außer bei <dialog> oder [autofocus]) • Fokus bewegt sich automatisch raus • Kein Light-Dismiss on blur
Das Popover kommt in den Tab-Reihenfolge direkt nach dem Button auch wenn es im DOM nicht so abgebildet ist. Das beeinflusst aber nicht den Accessibility Tree.
Wenn zwischen dem Button und dem Popover interaktive Elemente sind und das Popover geschlossen ist, bewegt sich der Fokus zum nächsten Button. Wenn aber das Popover offen ist bewegt sich der Fokus vom Button in das Popover, geht dort über alle interaktiven Elemente und bewegt sich dann dann erst zum nächsten Button in der DOM-Reihenfolge. Das betrifft aber nur die Tab-Reihenfolge und nicht den Accessibility Tree. Wenn man also den virtuellen Cursor verwendet kommt man immer vom Button zum nächsten Button auch wenn das Popover offen ist.
• Fokus bewegt sich nicht automatisch rein (außer bei <dialog> oder [autofocus]) • Fokus bewegt sich automatisch raus • Kein Light-Dismiss on blur •
Hier noch mal ein weiteres Popover-Beispiel um zu zeigen, dass man es auch mit einem Dialog verwenden kann.
<button popovertarget=”dialog1”> Login </button>
<dialog popover id=”dialog1”> <h2>Login</h2> <div> <label for=”name”>Username</label> <input type=”text” id=”name”> </div> <button>Login</button> </dialog>Hier noch mal zwei Popover in Action. Beim ersten öffnet sich eine einfache Gruppe. Da wird der Fokus nicht verschoben. Dann öffne ich das zweite Popover mit einem Dialog und Focus geht automatisch ins erste fokussiertere Element. Man sieht, dass die standardmäßig zentriert ausgerichtet sind, aber ich habe gesagt, dass sie normalerweise am Button oder Element, das sie öffnet angehängt sind.
Die Ausrichtung könnte man sehr elegant über CSS Anchor Positioning steuern. Aktuell unterstützt leider nur Chrome dieses Feature.
[popovertarget] { anchor-name: —button; } [popover] { position-anchor: —button; inset-block-start: anchor(end); inset-inline-start: anchor(end); margin: 0; }
Vielen Dank! accessibility-cookbook.com matuzo.social htmhell.dev matuzo.at manuel@matuzo.at Danke für die Aufmerksamkeit.