Alle interaktiven Elemente per Tastatur erreichbar zu machen ist die Grundvoraussetzung barrierefreier Websites. Drei Werkzeuge tragen dabei die Hauptarbeit: ein Skip-Link, das tabindex-Attribut und das inert-Attribut. Sie adressieren unterschiedliche Probleme, aber nur zusammen ergeben sie eine schlüssige Lösung.
Screenreader-Nutzende und Tastaturnutzende durchlaufen beim Seitenaufruf die komplette Navigation, bevor sie zum Hauptinhalt gelangen, es sei denn, es gibt einen Sprunglink. Der Skip-Link ist das einfachste und wirkungsvollste Barrierefreiheits-Pattern überhaupt.
<a href="#main-content" class="skip-link">Zum Hauptinhalt springen</a>
<header>
<nav>...</nav>
</header>
<main id="main-content" tabindex="-1">
...
</main>
Das tabindex=„-1“ auf <main> macht das Ziel des Skip-Links programmatisch fokussierbar, ohne es in den normalen Tab-Fluss aufzunehmen. Das ist wichtig, weil ein Sprunglink zwar optisch zum Ziel springen kann, der Tastaturfokus aber nicht in jedem Kontext zuverlässig dort landet. Gerade für Tastaturnutzende entscheidet dieser Unterschied darüber, ob sie wirklich im Hauptinhalt weiterarbeiten können, oder nach dem Sprung erneut an der falschen Stelle der Seite stehen.
Das CSS hält den Link unsichtbar bis er Fokus erhält:
.skip-link {
position: absolute;
top: -100%;
left: 1rem;
padding: 0.5rem 1.25rem;
background: var(--color-primary, #003366);
color: #fff;
border-radius: 0 0 4px 4px;
text-decoration: none;
z-index: 1000;
transition: top 0.1s;
}
.skip-link:focus {
top: 0;
}
Der Browser folgt beim Tab-Sprung der Reihenfolge im DOM, nicht der visuellen Darstellung. CSS-Eigenschaften wie order in Flexbox oder grid-row können visuelle Anordnung und DOM-Ordnung auseinanderdriften lassen. Das Ergebnis: Der Fokus springt auf dem Bildschirm.
Positive tabindex-Werte (tabindex=„1“, tabindex=„2“ etc.) sind ein Anti-Pattern: Sie unterbrechen den natürlichen Fluss und erzeugen schwer wartbare Abhängigkeiten. Die Lösung ist immer eine korrekte DOM-Reihenfolge, nicht eine erzwungene tabindex-Hierarchie.
Native HTML-Elemente wie <button>, <a> und <input>sind von Haus aus fokussierbar. Wer stattdessen <div> als interaktive Elemente verwendet, muss Fokussierbarkeit manuell herstellen.
<div
class="card"
role="button"
tabindex="0"
onclick="handleClick()"
onkeydown="if(event.key==='Enter'||event.key===' ')handleClick()"
>
Karteninhalt
</div>
Das tabindex=„0“ fügt das Element in den natürlichen Tab-Fluss ein. Das role=„button“ teilt Screenreadern mit, dass es sich um eine Schaltfläche handelt. Ohne onkeydown lässt es sich zwar fokussieren, aber nicht per Tastatur auslösen. Die klar bessere Lösung ist meistens das native <button>. Dann entfallen Rolle, tabindex und Keyboard-Handler.
tabindex=„-1“ macht ein Element fokussierbar per JavaScript, ohne dass es im Tab-Fluss erscheint. Der typische Einsatz: nach dem Öffnen eines Modals soll der Fokus ins Modal wandern.
function openModal() {
const modal = document.getElementById('modal');
modal.removeAttribute('hidden');
// Fokus auf erstes fokussierbares Element im Modal setzen
const first = modal.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
first?.focus();
}
function closeModal(triggerElement) {
const modal = document.getElementById('modal');
modal.setAttribute('hidden', '');
// Fokus zurück zum auslösenden Element
triggerElement?.focus();
}
Beim Schließen ist das Zurücksetzen des Fokus ebenso wichtig wie das Setzen beim Öffnen. Wer das vergisst, schickt Tastaturnutzende zurück an den Anfang der Seite.
Das Beispiel zeigt nur das Grundprinzip des Fokus-Managements. Für echte Modals kommen weitere Anforderungen hinzu: Der Fokus muss innerhalb des Dialogs gehalten werden, Escape sollte das Modal schließen, und semantisch gehören role=„dialog“ sowie in vielen Fällen aria-modal=„true“ dazu.
Das inert-Attribut entfernt ein Element und alle seine Kinder gleichzeitig aus dem Tab-Fluss und aus dem Accessibility Tree. Es ist stärker als tabindex=„-1“: Screenreader nehmen das Element nicht mehr wahr, Klicks werden ignoriert.
Der klassische Anwendungsfall ist die Off-Canvas-Navigation, die ausgeblendet bleibt, bis sie geöffnet wird.
<nav id="main-nav" inert aria-hidden="true">
<ul>
<li><a href="/ueber-uns">Über uns</a></li>
<li><a href="/leistungen">Leistungen</a></li>
</ul>
</nav>
<button onclick="toggleNav()">Menü</button>
function toggleNav() {
const nav = document.getElementById('main-nav');
const isOpen = !nav.hasAttribute('inert');
if (isOpen) {
nav.setAttribute('inert', '');
nav.setAttribute('aria-hidden', 'true');
} else {
nav.removeAttribute('inert');
nav.removeAttribute('aria-hidden');
nav.querySelector('a')?.focus();
}
}
inert ist seit 2023 in modernen Browsern breit verfügbar. Für ältere Umgebungen steht das WICG-Polyfill bereit: github.com/WICG/inert. Es bildet das Verhalten weitgehend nach, hat aber wie jedes Polyfill Grenzen: Bei dynamisch eingefügten Inhalten und sehr großen DOM-Bäumen sollte man Timing und Performance im Blick behalten.
aria-hidden=„true“ kann in Legacy-Setups zusätzlich verwendet werden, ist aber bei nativer inert-Unterstützung weitgehend redundant: inert entfernt das Element bereits vollständig aus dem Accessibility Tree. Als Fallback-Schicht für ältere Browser bleibt es sinnvoll, solange sichergestellt ist, dass kein fokussierbares Element im betroffenen Bereich erreichbar bleibt.
Das folgende Muster zeigt, wie inert, aria-expanded und Fokus-Management in einer realen Navigation zusammenspielen. Hauptmenüpunkte sind per Tab erreichbar. Enter oder Leertaste öffnen ein Untermenü und setzen den Fokus auf den ersten Eintrag. Tab navigiert anschließend durch die Untermenüpunkte. Escape schließt das Untermenü und gibt den Fokus zurück an den auslösenden Button.
Wichtig: inert steuert nicht die visuelle Darstellung. Es verhindert Fokus, Klicks und die Ausgabe im Accessibility Tree. Ob ein Untermenü sichtbar ist, muss zusätzlich über CSS oder das hidden-Attribut geregelt werden.
<nav aria-label="Hauptnavigation">
<ul>
<li>
<button
aria-expanded="false"
aria-controls="submenu-loesungen"
onclick="toggleSubmenu(this)"
>
Lösungen
</button>
<ul id="submenu-loesungen" hidden inert>
<li><a href="/arztpraxis">Arztpraxis, MVZ und Klinik</a></li>
<li><a href="/immobilien">Immobilienprojekte</a></li>
</ul>
</li>
<li><a href="/verbaende">Verbände</a></li>
<li><a href="/blog">Blog</a></li>
</ul>
</nav>
function toggleSubmenu(trigger) {
const submenu = document.getElementById(trigger.getAttribute('aria-controls'));
const isExpanded = trigger.getAttribute('aria-expanded') === 'true';
isExpanded ? closeSubmenu(trigger, submenu) : openSubmenu(trigger, submenu);
}
function openSubmenu(trigger, submenu) {
trigger.setAttribute('aria-expanded', 'true');
submenu.hidden = false;
submenu.removeAttribute('inert');
submenu.querySelector('a, button')?.focus();
}
function closeSubmenu(trigger, submenu) {
trigger.setAttribute('aria-expanded', 'false');
submenu.setAttribute('inert', '');
submenu.hidden = true;
trigger.focus();
}
document.addEventListener('keydown', (e) => {
if (e.key !== 'Escape') return;
const expanded = document.querySelector('[aria-expanded="true"]');
if (!expanded) return;
const submenu = document.getElementById(expanded.getAttribute('aria-controls'));
closeSubmenu(expanded, submenu);
});
Solange ein Untermenü inert trägt, sind seine Links für Tab-Nutzende und assistive Technologien nicht erreichbar. Der sichtbare Zustand wird im Beispiel zusätzlich über hidden gesteuert. Der offene Zustand ist über aria-expanded="true" maschinenlesbar kommuniziert. aria-controls verknüpft den Button mit dem Untermenü-Element und macht die Beziehung zwischen Trigger und gesteuertem Bereich nachvollziehbar.
Für klassische Website-Navigationen ist dieses Disclosure-Pattern meist die robustere Lösung als eine vollständige ARIA-Menubar. role=„menubar“ , role=„menuitem“ und Pfeiltasten-Handling können sinnvoll sein, wenn eine Navigation wie eine Anwendungsmenüleiste funktioniert. Für normale Webseitenmenüs erzeugen sie aber oft mehr Komplexität als Nutzen. Entscheidend ist nicht maximale ARIA-Ausstattung, sondern ein vorhersehbares, testbares Bedienmuster.
| Werkzeug | Wann |
|---|---|
| Skip-Link | Immer, auf jeder Seite |
| tabindex="0" | Custom-Elemente, die fokussierbar sein sollen |
| tabindex="-1" | Programmatisches Fokus-Management (Modals, Alerts) |
| inert | Versteckte Bereiche komplett aus Tab-Fluss und Screenreader nehmen |
Technische Spezifikation des W3C, die HTML um Zugänglichkeitsattribute erweitert. Ermöglicht es, interaktive Zustände und Beziehungen maschinenlesbar zu beschreiben: aria-expanded kommuniziert, ob ein Bereich geöffnet oder geschlossen ist; aria-controls verknüpft ein Steuerelement mit dem Element, das es kontrolliert; aria-hidden blendet Inhalte aus dem Accessibility Tree aus. Grundregel: Wo natives HTML ausreicht, ist ARIA nicht nötig.
Die strukturierte Darstellung eines HTML-Dokuments als Baumstruktur im Browser. Elemente, ihre Eigenschaften und ihre Beziehungen zueinander sind über JavaScript les- und veränderbar. Für Tastaturnavigation gilt: Der Browser folgt beim Tab-Sprung der Reihenfolge im DOM, nicht der visuellen Darstellung. Geraten DOM-Ordnung und visuelles Layout auseinander, springt der Fokus auf dem Bildschirm.
Eine parallele Baumstruktur, die der Browser aus dem DOM ableitet und an assistive Technologien wie Screenreader übergibt. Sie enthält nur zugänglichkeitsrelevante Informationen: Rollen, Namen, Zustände und Eigenschaften der Elemente. Das inert-Attribut entfernt Elemente vollständig aus dem Accessibility Tree; Screenreader nehmen sie nicht mehr wahr, Klicks und Fokus werden unterbunden.