mit Tailwind CSS für alle Nutzer verständlich
Ein Tooltip, der nur bei Mausbewegung erscheint, bleibt für Tastaturnutzer und Screenreader unsichtbar. Barrierefreie Tooltips mit Tailwind CSS verknüpfen Zusatzinformationen über aria-describedby, reagieren auf Hover und Fokus gleichermaßen und schließen sich zuverlässig per Escape.
Inhaltsverzeichnis
- 1. Warum Tooltips oft unsichtbar für Screenreader bleiben
- 2. aria-describedby: Tooltip Text mit dem Trigger verknüpfen
- 3. Hover und Fokus: warum beide Trigger nötig sind
- 4. Escape zum Schließen und Fokus-Rückgabe
- 5. role tooltip versus das native title Attribut
- 6. Popover mit mehr Inhalt: aria-expanded und aria-haspopup
- 7. Positionierung ohne Tastaturfalle
- 8. Mobile und Touch: Tooltips ohne Hover Konzept
- 9. Tooltip Patterns im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Tooltips oft unsichtbar für Screenreader bleiben
Ein selbst gebauter Tooltip aus einem <div>, das nur bei :hover per CSS eingeblendet wird, existiert für zwei Nutzergruppen praktisch nicht: Tastaturnutzer, die das auslösende Element fokussieren, aber niemals mit der Maus darüberfahren, und Screenreader Nutzer, für die eine rein visuelle Ein- und Ausblendung ohne semantische Verknüpfung unsichtbar bleibt. Beide Gruppen verpassen damit exakt die Zusatzinformation, die ein Tooltip eigentlich liefern soll, etwa die Erklärung eines Fachbegriffs oder eines Icon Buttons.
Tailwind CSS liefert keine eigene Tooltip Komponente mit, was Entwicklern die volle Kontrolle über die semantische Umsetzung überlässt, gleichzeitig aber auch die volle Verantwortung dafür überträgt. Ein rein mit group-hover:block gebautes Tooltip Muster sieht optisch überzeugend aus, bleibt aber ohne zusätzliche ARIA Verknüpfung und Tastatur Unterstützung für einen relevanten Teil der Nutzerschaft unbrauchbar. Dieser Artikel zeigt, wie barrierefreie Tooltips und Popover mit Tailwind CSS in der Praxis entstehen.
2. aria-describedby: Tooltip Text mit dem Trigger verknüpfen
Die Grundlage jedes barrierefreien Tooltips ist die programmatische Verknüpfung zwischen dem auslösenden Element und dem Tooltip Text über aria-describedby. Ohne diese Verknüpfung liest ein Screenreader beim Fokussieren des Elements nur dessen eigenen, sichtbaren Text vor, der zusätzliche Tooltip Inhalt bleibt unerwähnt, selbst wenn er visuell direkt daneben erscheint. aria-describedby verweist auf die id des Tooltip Elements und sorgt dafür, dass der Text automatisch nach dem Hauptinhalt vorgelesen wird.
Wichtig ist, dass das Tooltip Element selbst im DOM vorhanden bleibt, auch wenn es visuell über Tailwind Utilities wie opacity-0 und invisible ausgeblendet ist. aria-describedby funktioniert nur, wenn das referenzierte Element tatsächlich existiert, ein per x-if komplett aus dem DOM entferntes Tooltip Element würde die Verknüpfung ins Leere laufen lassen, sobald der Tooltip gerade nicht sichtbar ist.
<!-- Tooltip element stays in the DOM (opacity/visibility toggled),
aria-describedby links it to the trigger regardless of visual state -->
<div class="relative inline-block group">
<button
type="button"
aria-describedby="tooltip-shipping"
class="text-slate-500 hover:text-slate-700"
>
<svg class="w-4 h-4" aria-hidden="true"><!-- info icon --></svg>
<span class="sr-only">Info zu Versandkosten</span>
</button>
<div
id="tooltip-shipping"
role="tooltip"
class="absolute bottom-full mb-2 invisible opacity-0 group-hover:visible
group-hover:opacity-100 group-focus-within:visible
group-focus-within:opacity-100 transition-opacity
bg-slate-800 text-white text-xs rounded-lg px-3 py-2 w-48"
>
Kostenloser Versand ab 50 Euro Bestellwert.
</div>
</div>
3. Hover und Fokus: warum beide Trigger nötig sind
Ein Tooltip, der ausschließlich auf :hover reagiert, bleibt für Tastaturnutzer komplett unerreichbar, weil sie das auslösende Element niemals mit der Maus berühren. Die Tailwind Utility group-focus-within: löst dieses Problem elegant: Sobald das auslösende Element per Tab Taste fokussiert wird, erhält der Tooltip dieselbe sichtbare Darstellung wie bei Hover, ohne dass zusätzliches JavaScript für die reine Sichtbarkeitssteuerung nötig wäre.
Ein zweiter, oft übersehener Aspekt: Der Tooltip muss auch bei Mausbewegung innerhalb des Tooltips selbst sichtbar bleiben, falls er interaktive Inhalte wie einen Link enthält. Ohne diese Berücksichtigung verschwindet der Tooltip, sobald die Maus vom auslösenden Element zum Tooltip Inhalt wandert, was WCAG Erfolgskriterium 1.4.13 "Content on Hover or Focus" explizit adressiert: Zusätzlicher Inhalt bei Hover oder Fokus muss hoverbar bleiben, solange der Zeiger über dem Inhalt verweilt.
<!-- Tailwind's group-focus-within makes the tooltip appear on keyboard
focus, exactly like it does on mouse hover -->
<div class="relative inline-block group">
<button
type="button"
aria-describedby="tooltip-discount"
class="underline decoration-dotted text-slate-700"
>
Mengenrabatt
</button>
<div
id="tooltip-discount"
role="tooltip"
class="absolute bottom-full mb-2 invisible opacity-0
group-hover:visible group-hover:opacity-100
group-focus-within:visible group-focus-within:opacity-100
transition-opacity bg-slate-800 text-white text-xs
rounded-lg px-3 py-2 w-56"
>
Ab 10 Stück gilt automatisch ein Rabatt von 5 Prozent.
</div>
</div>
4. Escape zum Schließen und Fokus-Rückgabe
Ein reiner CSS Tooltip über group-hover und group-focus-within schließt sich automatisch, sobald der Fokus das auslösende Element verlässt, was für einfache Tooltips meist ausreicht. Sobald ein Tooltip jedoch interaktive Inhalte enthält oder als komplexeres Popover mit JavaScript gesteuert wird, muss zusätzlich die Escape Taste zum Schließen unterstützt werden, mit anschließender Rückgabe des Fokus auf das auslösende Element. Ohne diese Unterstützung bleibt ein geöffnetes, JavaScript gesteuertes Popover unter Umständen dauerhaft sichtbar, selbst wenn der Nutzer es eigentlich schließen möchte.
Diese Anforderung ist keine Kür, sondern folgt direkt aus WCAG 1.4.13: Zusätzlicher Inhalt, der bei Hover oder Fokus erscheint, muss über einen einfachen Mechanismus wie Escape wieder entfernbar sein, ohne dass der Nutzer den Mauszeiger oder Fokus über eine bestimmte Distanz bewegen muss. Ein JavaScript gesteuertes Popover sollte deshalb immer einen globalen keydown Listener auf Escape registrieren, solange es geöffnet ist, und diesen beim Schließen wieder entfernen.
// Alpine.js popover: Escape closes it and returns focus to the trigger
function popover() {
return {
open: false,
togglePopover() {
this.open = !this.open;
},
onKeydown(event) {
if (event.key === 'Escape' && this.open) {
this.open = false;
this.$refs.trigger.focus();
}
},
};
}
5. role tooltip versus das native title Attribut
Das native HTML title Attribut erzeugt einen browserseitigen Tooltip, der auf den ersten Blick einfach wirkt, aber mehrere gravierende Barrierefreiheits Probleme mit sich bringt. Der browserseitige Tooltip erscheint erst nach einer Verzögerung, ist mit CSS nicht gestaltbar, funktioniert auf Touch Geräten meist gar nicht, und wird von vielen Screenreadern inkonsistent oder gar nicht vorgelesen. Aus diesen Gründen gilt title als unzuverlässige Grundlage für barrierefreie Tooltips und sollte durch ein selbst gebautes Muster mit role="tooltip" und aria-describedby ersetzt werden.
role="tooltip" signalisiert Screenreadern explizit, dass es sich um eine kurze, ergänzende Beschreibung handelt, nicht um interaktiven Inhalt. Diese Rolle ist ausschließlich für rein informative, nicht interaktive Tooltips gedacht. Sobald ein Tooltip Links, Buttons oder andere interaktive Elemente enthält, ist die Rolle tooltip nicht mehr passend, dann handelt es sich technisch um ein Popover oder Dialog Muster mit eigener Semantik.
6. Popover mit mehr Inhalt: aria-expanded und aria-haspopup
Sobald ein Tooltip über eine kurze Textbeschreibung hinausgeht und interaktive Elemente wie Links oder Buttons enthält, wird aus dem Tooltip technisch ein Popover. Das auslösende Element braucht dann aria-haspopup="true" oder einen spezifischeren Wert wie aria-haspopup="menu", um anzukündigen, dass eine Aktivierung ein zusätzliches Overlay öffnet. Zusätzlich signalisiert aria-expanded, ob das Popover aktuell geöffnet oder geschlossen ist, und muss bei jedem Öffnen und Schließen dynamisch aktualisiert werden.
Im Unterschied zum reinen Tooltip darf der Fokus in ein Popover mit interaktivem Inhalt tatsächlich hineinwandern, ein Nutzer muss den enthaltenen Link oder Button per Tab erreichen können. Das unterscheidet ein Popover grundlegend vom reinen role="tooltip" Muster, bei dem niemals ein eigenständiger Fokuswechsel in den Tooltip Inhalt hinein stattfinden soll.
<!-- Popover with interactive content needs aria-haspopup and dynamic
aria-expanded, unlike a plain informational tooltip -->
<div class="relative inline-block" x-data="popover()" x-on:keydown="onKeydown">
<button
type="button"
x-ref="trigger"
aria-haspopup="true"
x-bind:aria-expanded="open"
x-on:click="togglePopover()"
class="text-sm font-medium text-sky-700 underline"
>
Lieferoptionen anzeigen
</button>
<div
x-show="open"
x-transition
role="menu"
class="absolute z-10 mt-2 bg-white border border-slate-200 rounded-xl
shadow-lg p-4 w-64"
>
<a href="/versand" role="menuitem" class="block text-sky-700 underline mb-2">
Versandkosten im Detail
</a>
<a href="/abholung" role="menuitem" class="block text-sky-700 underline">
Abholung im Store
</a>
</div>
</div>
7. Positionierung ohne Tastaturfalle
Bei der Positionierung eines Tooltips oder Popovers nahe am Bildschirmrand verschieben viele Implementierungen das Overlay dynamisch mit JavaScript, damit es nicht abgeschnitten wird. Diese Positionslogik darf niemals mit der Fokus Reihenfolge interferieren, ein Popover, das visuell links vom Trigger erscheint, aber im DOM danach steht, darf die Tab Reihenfolge nicht verwirren. Die DOM Position sollte unabhängig von der visuellen Positionierung logisch bleiben, genau wie bei jeder anderen Form von CSS Umsortierung.
Eine zweite Falle entsteht, wenn ein reiner Informations Tooltip versehentlich fokussierbare Elemente enthält, etwa ein Icon mit eigenem tabindex="0" innerhalb des Tooltip Texts. Ein Nutzer, der mit Tab durch die Seite navigiert, würde dann unerwartet in den Tooltip Inhalt hineingezogen, obwohl dieser rein informativ gedacht war. Reine role="tooltip" Inhalte sollten deshalb niemals eigene fokussierbare Elemente enthalten, das ist ein klares Signal dafür, stattdessen ein Popover Muster mit aria-haspopup zu verwenden.
8. Mobile und Touch: Tooltips ohne Hover Konzept
Touch Geräte kennen kein echtes Hover Konzept, ein Finger berührt einen Bildschirm entweder oder er tut es nicht, es gibt keinen Zwischenzustand wie bei einem Mauszeiger. Ein Tooltip, der ausschließlich auf :hover reagiert, bleibt auf Touch Geräten deshalb faktisch nicht auslösbar. Die robuste Lösung ist, auf Touch Geräten explizit auf Tap Events zu reagieren und das Tooltip Muster in ein Toggle Verhalten zu überführen, ähnlich einem Popover, statt sich auf CSS Hover Zustände zu verlassen.
Eine praktikable Implementierung erkennt Touch Fähigkeit über window.matchMedia('(hover: none)') und wechselt für diese Geräte von reinem CSS Hover zu einem klick beziehungsweise tap gesteuerten Alpine.js Toggle mit sichtbarem Schließen Button. So bleibt der Tooltip Inhalt auch ohne Maus und ohne physische Tastatur erreichbar, ohne die Desktop Erfahrung mit Hover zu verkomplizieren.
// Detect touch-only devices and switch tooltip behavior from hover to tap
function adaptiveTooltip() {
return {
open: false,
isTouchDevice: window.matchMedia('(hover: none)').matches,
handleTrigger() {
if (this.isTouchDevice) {
this.open = !this.open;
}
// on hover-capable devices, CSS group-hover already handles visibility
},
};
}
9. Tooltip Patterns im Vergleich
Die folgende Übersicht zeigt gängige Tooltip Implementierungen und die jeweils empfohlene, barrierefreie Alternative.
| Bereich | Unsicher | Empfohlenes Pattern | Vorteil |
|---|---|---|---|
| Tooltip Grundlage | Natives title Attribut | role="tooltip" + aria-describedby |
Gestaltbar, zuverlässig vorgelesen |
| Trigger Event | Nur hover | hover + focus-within | Auch per Tastatur erreichbar |
| Schließen | Kein Escape Support | Escape mit Fokus-Rückgabe | WCAG 1.4.13 konform |
| Interaktiver Inhalt | Als reiner role tooltip | aria-haspopup Popover | Fokus kann sicher hineinwandern |
| Touch Geräte | Nur CSS Hover | Tap Toggle bei hover: none | Auch ohne Maus auslösbar |
Diese Gegenüberstellung zeigt, dass barrierefreie Tooltips selten an einer einzelnen Klasse scheitern, sondern an der konsequenten Kombination aus ARIA Verknüpfung, doppeltem Trigger Event und einem verlässlichen Schließen Mechanismus.
Mironsoft
Tailwind CSS, Barrierefreiheit und WCAG-konforme Frontend-Entwicklung
Tooltips, die für alle Nutzer funktionieren?
Wir prüfen bestehende Tooltip- und Popover-Muster auf fehlende aria-describedby-Verknüpfungen, fehlenden Escape-Support und Touch-Kompatibilität, und implementieren barrierefreie Alternativen mit Tailwind CSS und Alpine.js.
Tooltip-Audit
Prüfung aller Tooltip- und Popover-Muster auf ARIA-Konformität
Tastatur- und Touch-Support
Hover, Fokus und Tap in einem konsistenten Muster vereint
Screenreader Tests
Manuelle Prüfung aller Tooltips mit NVDA und VoiceOver
10. Zusammenfassung
Barrierefreie Tooltips mit Tailwind CSS beruhen auf drei Grundpfeilern: einer programmatischen Verknüpfung über aria-describedby oder role="tooltip", einem doppelten Trigger über hover und focus-within, und einem zuverlässigen Schließen Mechanismus per Escape mit Fokus-Rückgabe. Das native title Attribut erfüllt keine dieser Anforderungen zuverlässig und sollte durch ein selbst gebautes Muster ersetzt werden.
Sobald ein Tooltip interaktive Inhalte enthält, wird daraus technisch ein Popover mit eigener aria-haspopup und aria-expanded Semantik, in das der Fokus im Gegensatz zum reinen Tooltip aktiv hineinwandern darf. Touch Geräte ohne Hover Konzept brauchen ein Tap gesteuertes Toggle Verhalten, damit Tooltip Inhalte auch ohne Maus und Tastatur erreichbar bleiben.
Barrierefreie Tooltips mit Tailwind CSS - das Wichtigste auf einen Blick
aria-describedby statt title
Natives title Attribut vermeiden, stattdessen role tooltip mit aria-describedby verknüpfen.
hover UND focus-within
Beide Trigger Events nötig, sonst bleibt der Tooltip für Tastaturnutzer unerreichbar.
Escape zum Schließen
WCAG 1.4.13 verlangt einen einfachen Mechanismus zum Entfernen des Zusatzinhalts.
Popover statt Tooltip bei Interaktivität
Interaktive Inhalte brauchen aria-haspopup und aria-expanded, nicht role tooltip.