Snippet

Fokus-Indikatoren mit :focus-visible stylen

Oft werden die wichtigen Fokus-Indikatoren aus ästhetischen Gründen mit CSS entfernt. Besser wäre aber sie bewusst einzusetzen.

von Oli Feiler · 24. April 2026

Fokus-Indikatoren mit :focus-visible stylen

Der Fokus-Indikator ist für Tastaturnutzende das, was der Mauszeiger für alle anderen ist: die einzige Orientierung auf der Seite. Er wird entfernt, weil die Browser-Standarddarstellung ästhetisch unbefriedigend wirkt. Dieser Snippet zeigt, wie man ihn richtig ersetzt.

Das Anti-Pattern

            /* Steht in sehr vielen Stylesheets */
*:focus {
  outline: none;
}
        

Diese Zeile ist ein Barrierefreiheitsfehler. Sie entfernt für alle fokussierten Elemente jeden sichtbaren Hinweis auf den aktuellen Fokus. WCAG 2.4.7 (Focus Visible, Level AA) verlangt, dass Tastaturfokus sichtbar ist. Mit dieser Regel ist er es nicht.

:focus vs. :focus-visible

:focus greift bei jeder Art von Fokus: Mausklick, Tastatur, Touch. :focus-visible greift nur dann, wenn der Browser einschätzt, dass der Nutzer von einer sichtbaren Markierung profitiert. In der Praxis: bei Tastaturnavigation, nicht bei Mausklick.

Das robuste Muster nimmt den Fokus-Ring nur dort weg, wo :focus-visible nicht greift:

            /* Fokus-Ring nur bei Tastaturnavigation weg */
:focus:not(:focus-visible) {
  outline: none;
}

/* Eigener Ring für Tastaturnavigation */
:focus-visible {
  outline: 3px solid var(--focus-ring-outer);
  outline-offset: 3px;
}
        

Ein globales :focus{outline:none} ohne diesen Vorbehalt ist genau die Denkfigur, die das Problem erst erzeugt. Browser wenden :focus-visible-Logik auch bei Textfeldern an, wo der Zustand sichtbar bleiben soll, selbst wenn der Fokus per Maus gesetzt wurde. MDN weist darauf hin, dass Browser-Heuristiken hier kontextabhängig entscheiden.

Relevante WCAG-Kriterien

Drei Kriterien sind für selbst gestaltete Fokus-Indikatoren einschlägig.

WCAG 2.4.7 Focus Visible (Level AA): Der Tastaturfokus muss sichtbar sein. Das ist die Grundanforderung.

WCAG 1.4.11 Non-text Contrast (Level AA): Wer einen eigenen Fokus-Indikator gestaltet, muss einen Kontrast von mindestens 3:1 zur angrenzenden Farbe sicherstellen. Ausgenommen sind nur Browser-Standardstile, die der Autor nicht verändert hat. Das ist das AA-Kriterium für Kontrast bei Fokus-Indikatoren.

WCAG 2.4.11 Focus Not Obscured Minimum (Level AA): Ein fokussiertes Element darf nicht vollständig durch überlagernde Inhalte verdeckt werden. Sticky Header, Cookie-Banner und Overlays sind typische Problemfälle. Dazu mehr im Abschnitt weiter unten.

WCAG 2.4.13 Focus Appearance (Level AAA): Definiert konkrete Mindestmaße: Fokus-Indikator-Fläche entspricht mindestens einer 2px-Umrandung des Elements, Kontrast mindestens 3:1 zwischen fokussiertem und unfokussiertem Zustand. Dieses Kriterium ist AAA, eignet sich aber hervorragend als Qualitätsmaßstab für Design-Systeme. Ein Ring von 3px mit deutlichem Kontrast trifft diesen Standard und übertrifft ihn.

Ein Fokus-Indikator sollte außerdem nicht nur eine leichte Farbveränderung sein. Ring, Unterstreichung oder eine kontrastierende Fläche macht den Zustand unabhängig von Farbnuancen erkennbar.

Implementierung mit CSS Custom Properties

Ein Token-Set im Design-System steuert alle Fokus-Indikatoren konsistent. Für wechselnde Hintergründe, Bildkacheln oder dunkle Sections empfiehlt sich ein Zwei-Farben-Ring: Wenn die beiden Ringfarben mindestens 9:1 Kontrast zueinander aufweisen, ist garantiert, dass mindestens eine davon 3:1 zum Hintergrund erreicht. W3C Technique C40 beschreibt dieses Pattern explizit.

outline ist für Fokus-Indikatoren besser geeignet als border: Es nimmt keinen Platz im Layout ein und verursacht keine Verschiebungen des Seitenaufbaus, wenn ein Element fokussiert wird.

Für Textlinks im Fließtext, wo die Textfarbe bereits kontrastkonform ist, gibt es eine einfachere Alternative: currentColor sorgt dafür, dass die Outline automatisch dieselbe Farbe wie der Text erhält.

            a:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}
        

Auf Buttons mit heller Schrift auf dunklem Hintergrund funktioniert currentColor nicht zuverlässig. Dort braucht es den expliziten Zwei-Farben-Ring.

            :root {
  --focus-ring-inner: #ffffff;
  --focus-ring-outer: #003366;
  --focus-ring-width: 2px;
  --focus-ring-offset: 2px;
}

:focus:not(:focus-visible) {
  outline: none;
}

:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-inner);
  outline-offset: var(--focus-ring-offset);
  box-shadow: 0 0 0 calc(var(--focus-ring-width) * 2) var(--focus-ring-outer);
}

@media (forced-colors: active) {
  :focus-visible {
    outline: 3px solid Highlight;
    box-shadow: none;
  }
}
        

Der innere weiße Ring trennt den Fokus-Indikator vom Element, der äußere dunkle Ring sorgt für Sichtbarkeit auf hellen Hintergründen. Auf dunklem Hintergrund kehrt sich das Verhältnis um: Der weiße Ring wird zur Konturfarbe. Das forced-colors-Override schaltet auf Systemfarbe, weil box-shadow im Forced Colors Mode auf none gesetzt wird.

Typische Komponenten

Der globale :focus-visible-Ring ist die Basis. Komponenten dürfen ihn anpassen, aber nur um ihn zu verbessern. Farben kommen aus dem globalen Token-Set, Komponenten steuern Geometrie: Offset und Radius.

Fokussierbar sind immer die interaktiven Elemente innerhalb einer Komponente, nicht die Container selbst. li:focus-visible greift nicht, weil li kein fokussierbares Element ist. li a:focus-visible oder .nav a:focus-visible schon.

            /* Textlink im Fließtext */
a:focus-visible {
  outline-offset: 3px;
}

/* Normaler Button */
.button:focus-visible {
  outline-offset: 3px;
}

/* Icon-Button: kreisförmig, mehr Abstand */
.icon-button:focus-visible {
  outline-offset: 6px;
  border-radius: 999px;
}

/* Hauptnavigation */
.mainnav a:focus-visible {
  outline-offset: 4px;
}

/* Card mit Link-Wrapper: Fokus umschließt die ganze Karte */
.card-link:focus-visible {
  outline-offset: 8px;
  border-radius: var(--radius-card, 4px);
}

/* Formularfelder: Radius übernehmen, kleinerer Offset */
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline-offset: 2px;
  border-radius: inherit;
}

/* Skip-Link: sichtbar im Viewport, nicht im normalen Fluss */
.skip-link:focus-visible {
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 9999;
  outline-offset: 3px;
}

/* ARIA-Komponenten als Catch-all */
[role="tab"]:focus-visible,
summary:focus-visible,
.slider-control:focus-visible {
  outline-offset: 3px;
}
        

box-shadow als alleiniger Indikator

box-shadow folgt dem border-radius und sieht bei gerundeten Elementen oft besser aus als outline. Es hat aber eine kritische Schwäche: Im Forced Colors Mode wird box-shadow auf none gesetzt. Ein Fokus-Indikator, der ausschließlich über box-shadow realisiert wird, verschwindet genau dann, wenn Sichtbarkeit am wichtigsten ist.

W3C Technique C40 hält deshalb fest: outline:none nicht setzen, nur um auf box-shadow allein zu wechseln. Wenn box-shadow gestalterisch nötig ist, bleibt eine Outline als Fallback nötig.

Der Zwei-Farben-Ring oben kombiniert beides sauber: outline als zuverlässige Basis, box-shadow als gestalterische Ergänzung, forced-colors-Block setzt box-shadow zurück und hält outline aktiv.

Focus Not Obscured: Sticky Header und Overlays

WCAG 2.4.11 verlangt, dass ein fokussiertes Element nicht vollständig von überlagernden Inhalten verdeckt wird. Sticky Header, Cookie-Banner, Offcanvas-Menüs und Chat-Widgets sind typische Problemfälle. Ein sauber gestylter Fokus-Ring hilft wenig, wenn er beim Scrollen unter dem Header verschwindet.

            /* Scroll-Anker korrigieren: fokussierte Elemente landen
   unterhalb des Sticky Headers */
html {
  scroll-padding-top: calc(var(--header-height) + 1rem);
}

[id] {
  scroll-margin-top: calc(var(--header-height) + 1rem);
}
        

Das löst nicht alle Overlays, ist aber der CSS-seitige Eingriff mit dem größten Effekt. Modale Dialoge müssen zusätzlich Fokus-Management betreiben, damit fokussierbare Elemente nicht hinter dem Modal erreichbar sind.

High Contrast Mode

forced-colors:active signalisiert, dass das Betriebssystem eine eingeschränkte Farbpalette erzwingt. Windows Contrast Themes sind das häufigste Beispiel. Eigene Farben werden durch systemdefinierte Keywords überschrieben.

            @media (forced-colors: active) {
  :focus-visible {
    outline: 3px solid Highlight;
    box-shadow: none;
  }
}
        

Highlight ist für generische Fokus-Indikatoren semantisch die bessere Wahl als ButtonText, weil es die Systemfarbe für ausgewählte bzw. aktive Zustände abbildet. Wichtig: forced-color-adjust:none sehr zurückhaltend einsetzen, weil es die Nutzerpräferenz bewusst außer Kraft setzt.

:focus-within und :has(:focus-visible)

Zwei verwandte Pseudoklassen, die in bestimmten Situationen nützlich sind.

:focus-within greift auf einem Element, wenn es selbst oder eines seiner Kindelemente fokussiert ist, unabhängig davon, ob per Tastatur oder Maus. Typischer Einsatz: einen Navigations-Container hervorheben, solange ein Link darin fokussiert ist.

            /* Menüpunkt-Container hervorheben, solange ein Link fokussiert ist */
.mainnav li:focus-within {
  background: var(--color-nav-hover);
}
        

:has(:focus-visible) ist das modernere, präzisere Pendant: Es greift nur dann, wenn ein Kindelement per Tastatur fokussiert ist. Das fehlende focus-visible-within lässt sich damit sauber nachbauen.

            /* Card hervorheben, wenn ein enthaltener Link per Tastatur fokussiert ist */
.card:has(:focus-visible) {
  outline: var(--focus-ring-width) solid var(--focus-ring-outer);
  outline-offset: 4px;
  border-radius: var(--radius-card, 4px);
}
        

:has() wird seit 2023 von allen modernen Browsern unterstützt.

@supports-Fallback für ältere Browser

:focus-visible ist seit 2022 breit verfügbar. Wer ältere Browser absichern will, kann den progressiven Ansatz nutzen: erst einen einfachen :focus-Ring als Fallback definieren, dann das präzise Muster für Browser, die :focus-visible unterstützen.

            /* Fallback: alle Browser */
:focus {
  outline: 3px solid var(--focus-ring-outer);
  outline-offset: 3px;
}

/* Präzises Muster für moderne Browser */
@supports selector(:focus-visible) {
  :focus:not(:focus-visible) {
    outline: none;
  }

  :focus-visible {
    outline: var(--focus-ring-width) solid var(--focus-ring-inner);
    outline-offset: var(--focus-ring-offset);
    box-shadow: 0 0 0 calc(var(--focus-ring-width) * 2) var(--focus-ring-outer);
  }
}
        

Zusammenfassung

WasWarum
:focus:not(:focus-visible) statt :focusFokus-Ring nur gezielt entfernen
Nie outline: none ohne ErsatzWCAG 2.4.7 Focus Visible absichern
3:1 Kontrast zur UmgebungWCAG 1.4.11 Non-text Contrast
Mind. 3px Ring plus Offsetrobuste Praxis, orientiert an WCAG 2.4.13 AAA
Zwei-Farben-Ring prüfenfunktioniert auf wechselnden Hintergründen
forced-colors-Fallback mit HighlightFokus in Kontrastmodi sichtbar halten
box-shadow nie alleinwird in Forced Colors Mode entfernt
Sticky Header / Overlays testenWCAG 2.4.11 Focus Not Obscured beachten
:has(:focus-visible) für Containermodernes focus-visible-within

Mehr zu Barrierefreiheit

 alt=

Barrierefreiheit, aber richtig!

Wir entwickeln digitale Plattformen für Verbände und Unternehmen. Barrierefreiheit denken wir dabei von Anfang an mit: in Systemarchitektur, Design-System und CMS.
Oli Feiler, Experte für Barrierefreiheit