Komponenten · Barrierefreiheit nach Framework
Barrierefreiheit mit Tailwind CSS
Tailwind ist kein JavaScript-Framework, sondern CSS – und erzeugt trotzdem wiederkehrende Barrieren, weil es Entscheidungen bequem macht, die man nicht bequem treffen sollte. Die drei häufigsten: der mit outline-none entfernte Fokusring, zu helle Grautöne aus der Standardpalette und Animationen ohne Rücksicht auf prefers-reduced-motion. Alle drei sind Konfigurationsfragen, keine Grenzen des Werkzeugs.
Diese Seite behandelt die Tailwind-spezifischen Fälle. Die vier Probleme, die in jedem Framework gleich sind, stehen unter Barrierefreiheit nach Framework.
Das Wichtigste in Kürze
-
outline-noneohne Ersatz ist der häufigste Tailwind-Befund überhaupt – und ein klarer Verstoß gegen WCAG 2.4.7 (Stufe A). -
focus-visible:stattfocus:zeigt den Ring nur bei Tastaturbedienung – der Kompromiss, der beide Seiten zufriedenstellt. -
sr-onlyist korrekt umgesetzt und der richtige Weg für versteckte Beschriftungen;hiddenundinvisiblesind es nicht. - Die Standardpalette ist nicht kontrastsicher.
text-gray-400auf Weiß liegt deutlich unter 4,5:1. -
motion-safe:undmotion-reduce:setzenprefers-reduced-motiondirekt in Utility-Klassen um. - Utility-Klassen ersetzen keine Semantik: Ein
divmitcursor-pointerbleibt eindiv. - Der Dark Mode verdoppelt die Kontrastprüfung – beide Themes müssen bestehen.
- Stand August 2026.
Der Fokusring: das eine große Thema
Kein anderes Werkzeug macht es so leicht, den Fokusring zu entfernen: outline-none ist eine Klasse. Und weil der Standard-Ring in vielen Designs stört, steht sie in sehr vielen Tailwind-Projekten.
Das Ergebnis ist ein Verstoß gegen 2.4.7 Fokus sichtbar auf Stufe A – und praktisch eine Anwendung, die mit der Tastatur unbenutzbar ist, weil niemand mehr sieht, wo er steht.
<!-- Falsch: Fokus ersatzlos entfernt -->
<button class="outline-none rounded bg-blue-600 px-4 py-2 text-white">
Speichern
</button>
<!-- Richtig: eigener Ring, nur bei Tastaturbedienung -->
<button
class="rounded bg-blue-600 px-4 py-2 text-white
focus-visible:outline-none
focus-visible:ring-2 focus-visible:ring-offset-2
focus-visible:ring-blue-900">
Speichern
</button>
Zwei Details machen den Unterschied. focus-visible: statt focus: blendet den Ring nur ein, wenn der Fokus über die Tastatur kam – das ist der Grund, aus dem der Ring ursprünglich entfernt wurde, und er entfällt damit. Und ring-offset-2 setzt den Ring mit Abstand um das Element, sodass er auf gleichfarbigem Hintergrund nicht verschwindet.
Wer den Ring einmal zentral definiert – in Tailwind über eine Komponentenklasse oder eine @layer base-Regel für :focus-visible –, hat das Thema für das ganze Projekt erledigt. Was ein brauchbarer Fokusindikator leistet, steht unter Tastaturbedienung & sichtbarer Fokus.
sr-only: richtig gut, oft falsch benutzt
Die Klasse sr-only ist eine korrekt umgesetzte Versteck-Technik: Das Element bleibt im Accessibility Tree, ist aber optisch nicht sichtbar. Genau das braucht man für Beschriftungen ohne sichtbaren Text.
<!-- Suchfeld mit Lupensymbol: das Label ist da, nur unsichtbar -->
<label for="suche" class="sr-only">Suchbegriff</label>
<input id="suche" type="search" class="rounded border px-3 py-2" />
<!-- Icon-Button: der Name kommt aus dem versteckten Text -->
<button class="rounded p-2">
<svg aria-hidden="true" class="h-5 w-5">…</svg>
<span class="sr-only">Menü öffnen</span>
</button>
Die zwei häufigen Fehler: hidden statt sr-only entfernt das Element aus dem Baum – dann ist auch der Name weg. Und sr-only an einem fokussierbaren Element erzeugt einen Tab-Stopp, den niemand sieht; dafür gibt es focus:not-sr-only, das etwa Skip-Links bei Fokus sichtbar macht.
Wann ein verstecktes Label und wann aria-label die richtige Wahl ist, steht unter Labels & Beschriftungen.
Die Standardpalette ist nicht kontrastsicher
Tailwinds Grauabstufungen sind gleichmäßig verteilt, nicht kontrastgeprüft. Auf weißem Grund liegt Fließtext erst ab etwa gray-600 sicher über 4,5:1; die beliebten gray-400- und gray-500-Töne für Hilfetexte, Platzhalter und deaktivierte Zustände liegen darunter.
Praktische Konsequenzen:
- Platzhaltertext ist kontrastpflichtig, sobald er Information trägt – ein Grund mehr, ihn nicht als Label zu missbrauchen.
- Deaktivierte Bedienelemente sind von der Kontrastanforderung ausgenommen, aber wenn niemand mehr lesen kann, was deaktiviert ist, hilft die Ausnahme keinem.
- Rahmen und Trennlinien um Bedienelemente brauchen 3:1 nach 1.4.11 –
border-gray-200reicht dafür nicht.
Der wirksame Schritt ist, im eigenen Theme nur geprüfte Werte freizugeben und die kritischen Töne gar nicht erst anzubieten. Die Werte prüfst du mit dem Kontrast-Check, die Systematik steht unter Farbkontraste.
Im Dark Mode verdoppelt sich die Prüfung: Jede Farbkombination muss in beiden Themes bestehen, und hell auf dunkel verhält sich nicht spiegelbildlich – ein Ton, der auf Weiß knapp besteht, fällt invertiert oft durch.
Bewegung: motion-safe und motion-reduce
Tailwind bringt die Medienabfrage als Variante mit, was sie praktisch kostenlos macht:
<!-- Animation nur, wenn Bewegung nicht abbestellt wurde -->
<div class="motion-safe:animate-pulse rounded bg-gray-200 p-4">Lädt …</div>
<!-- Oder umgekehrt: Übergang bei reduzierter Bewegung abschalten -->
<div class="transition-transform duration-300 motion-reduce:transition-none">…</div>
Die Faustregel: motion-safe: für alles, was sich von selbst bewegt, motion-reduce: für Übergänge, die bleiben dürfen, aber kürzer sein sollten. Wen das betrifft, steht unter Kognition & Neurodiversität.
Ein kontrastsicheres Theme aufsetzen
Der wirksamste Schritt ist, die problematischen Töne gar nicht erst zur Verfügung zu stellen. Statt die Standardpalette zu erweitern, definiert man eine kleine, geprüfte Menge semantischer Farben – dann ist jede Klasse, die jemand schreiben kann, bereits geprüft:
// tailwind.config.js – geprüfte Werte statt der vollen Palette
export default {
theme: {
extend: {
colors: {
text: {
DEFAULT: '#1f2933', // 14,5:1 auf Weiß
muted: '#52606d', // 7,1:1 auf Weiß – auch für Hilfetexte sicher
},
// bewusst kein "text-light": alles darüber fällt durch
rand: '#7b8794', // 3,4:1 – reicht für 1.4.11
},
},
},
};
Zwei Regeln haben sich dabei bewährt. Namen nach Zweck, nicht nach Helligkeit: Wer text-muted schreibt, denkt über die Rolle nach; wer text-gray-400 schreibt, über den Farbton. Und kein Ton unter der Schwelle im Theme: Was nicht existiert, kann niemand versehentlich benutzen. Die Werte prüfst du vorher einmal mit dem Kontrast-Check.
Für den Dark Mode gilt dasselbe zweimal – am einfachsten über CSS-Variablen, die beide Themes bedienen, statt über dark:-Varianten an jeder einzelnen Klasse.
Die drei Dinge, die ein Review immer prüfen sollte
Ein Tailwind-Review braucht keine lange Liste. Drei Suchen im Projekt decken den Großteil ab:
-
outline-noneohne begleitendesfocus-visible:ring– der Verstoß gegen 2.4.7, und der einzige, der sich per Textsuche zuverlässig finden lässt. -
cursor-pointeran einem Element, das kein<a>oder<button>ist – fast immer ein nachgebautes Bedienelement. -
text-gray-400,text-gray-300undplaceholder-gray-400auf hellem Grund – die Kontrastfälle, die im Entwurf niemandem auffallen.
Alle drei sind mit grep zu finden und in Minuten zu beheben – deutlich billiger als derselbe Befund in einem Prüfbericht.
Die Klassen, die Semantik entfernen
Zwei Utility-Klassen verändern nicht nur die Darstellung, sondern das, was Hilfsmittel melden. Das ist die unangenehmste Kategorie, weil im Code alles richtig aussieht.
list-none setzt list-style: none. In WebKit – also Safari am Mac und alle Browser auf iOS – entfernt das die Listensemantik: VoiceOver sagt „Liste mit 5 Einträgen“ dann nicht mehr an. Bei einer Navigation ist das der Unterschied zwischen „ich weiß, wie viele Punkte kommen“ und „ich taste mich durch“. Der Ausweg ist eine explizite Rolle:
<!-- In WebKit ohne Listensemantik -->
<ul class="list-none">…</ul>
<!-- Semantik zurückgeholt -->
<ul role="list" class="list-none">…</ul>
Das gilt für jede Liste, die ihre Aufzählungszeichen verliert – Navigationen, Karten-Raster, Tag-Leisten. Warum Listen überhaupt zählen, steht unter Listen richtig nutzen.
appearance-none entfernt die native Darstellung von Formularelementen. Die Semantik bleibt zwar, aber Zustände verschwinden optisch: Eine Checkbox mit appearance-none und ohne eigenes Häkchen ist nicht mehr erkennbar an oder aus – ein Verstoß gegen 1.4.1 Benutzung von Farbe, sobald nur noch die Füllfarbe den Zustand trägt. Die Umsetzung steht unter Toggle-Switches & Custom-Checkboxen.
Häufiger Fehler in der Praxis
cursor-pointer statt <button>. Ein div sieht damit klickbar aus und ist es für Tastatur und Screenreader trotzdem nicht – siehe Buttons vs. Links.
Überschriftenebene nach Schriftgröße wählen. In Tailwind ist die Größe eine Klasse, also frei wählbar. Genau deshalb rutscht die Ebene gern nach Optik statt nach Struktur – siehe Überschriften-Hierarchie.
hidden für Aufklapp-Inhalte. Funktioniert, entfernt aber das Element vollständig. Für ein Akkordeon ist das in Ordnung, für ein Element, das den Fokus hält, nicht.
focus: statt focus-visible:. Der Ring erscheint dann auch beim Mausklick – und genau das führt in der nächsten Designrunde dazu, dass er wieder entfernt wird.
Häufige Fragen
Ist Tailwind schlecht für Barrierefreiheit?
Nein. Es ist CSS und verhält sich neutral – es macht nur bestimmte Fehler bequemer als andere Ansätze. Ein einmal zentral definierter Fokusstil und ein geprüftes Theme nehmen den größten Teil des Risikos.
Wie erzwinge ich einen Fokusring projektweit?
Über eine Basisregel für :focus-visible in @layer base, ergänzt um ein Lint- oder Review-Verbot für outline-none ohne begleitendes focus-visible:ring-*.
Reicht sr-only für alle versteckten Texte?
Für Texte, die Hilfsmittel hören sollen: ja. Für Inhalte, die niemand bekommen soll: nein – dafür ist hidden richtig. Der Unterschied wird unter Screenreader-Grundlagen erklärt.
Gilt das auch für andere Utility-CSS-Ansätze?
Ja, weitgehend. Die Befunde hängen nicht an Tailwind, sondern daran, dass Utility-Klassen Darstellung ohne Semantik beschreiben – und dass der Fokusring eine Klasse entfernt.
Verwandte Themen
- Barrierefreiheit nach Framework – die vier gemeinsamen Probleme
- Tastaturbedienung & sichtbarer Fokus – was ein Fokusindikator leisten muss
- Farbkontraste – die Werte hinter der Palette
- Barrierefreiheit im UX- und UI-Design – wo diese Entscheidungen eigentlich fallen
Quellen
- Tailwind CSS: Screen Readers – sr-only und not-sr-only
- Tailwind CSS: prefers-reduced-motion – motion-safe und motion-reduce
- Understanding SC 2.4.7: Focus Visible (W3C)