Das ARIA-Combobox-Pattern korrekt und ohne Fokus-Chaos umsetzen
Ein Suchfeld mit Live-Vorschlägen sieht für sehende Nutzer selbsterklärend aus, ist aber ohne das ARIA-Combobox-Pattern für Tastatur- und Screenreader-Nutzer oft schlicht unbedienbar.
Inhaltsverzeichnis
- 1. Warum klassische Such-Widgets das ARIA-Combobox-Pattern brauchen
- 2. Grundstruktur: role="combobox", aria-expanded, aria-controls
- 3. aria-activedescendant statt Fokus-Verschiebung
- 4. Pfeiltasten-Navigation durch die Vorschläge implementieren
- 5. Screenreader-Ansage der Trefferanzahl
- 6. Suchbegriff-Highlighting ohne Screenreader-Rauschen
- 7. Abgrenzung zu Datepicker- und klassischen Combobox-Patterns
- 8. Magento-Hyvä-Live-Search barrierefrei umsetzen
- 9. Häufige Fehler bei Autocomplete-Implementierungen
- 10. Zusammenfassung
- 11. FAQ
1. Warum klassische Such-Widgets das ARIA-Combobox-Pattern brauchen
Ein einfaches Textfeld mit einer darunter eingeblendeten Vorschlagsliste sieht für sehende Nutzer wie ein zusammenhängendes Element aus. Technisch handelt es sich jedoch um zwei getrennte DOM-Bausteine, ein input-Element und eine Liste, deren Beziehung ohne zusätzliche ARIA-Attribute für Screenreader nicht erkennbar ist.
Ohne das Combobox-Pattern erfährt ein Screenreader-Nutzer nicht, dass beim Tippen neue Vorschläge erscheinen, kann diese nicht sinnvoll durchsuchen und weiß nicht, welcher Vorschlag gerade als aktiv markiert ist. Das Ergebnis ist ein Suchfeld, das rein visuell funktioniert, aber für einen ganzen Teil der Nutzerschaft unbrauchbar bleibt.
Das W3C ARIA Authoring Practices Guide definiert für genau diesen Fall das Combobox-Pattern mit einer festen Rollenstruktur. Es lohnt sich, dieses Pattern exakt zu befolgen, statt eine eigene Variante zu entwickeln, da Screenreader ihre Ansagen genau auf diese Struktur abstimmen.
2. Grundstruktur: role="combobox", aria-expanded, aria-controls
Das Eingabefeld selbst erhält role="combobox", auch wenn es sich technisch um ein normales input-Element handelt. Zusätzlich zeigt aria-expanded an, ob die Vorschlagsliste gerade sichtbar ist, und aria-controls verweist auf die id der Liste, damit die Beziehung zwischen beiden Elementen für Screenreader eindeutig ist.
Die Vorschlagsliste selbst erhält role="listbox", jeder einzelne Vorschlag darin role="option". Diese Struktur ist unabhängig davon einzuhalten, ob die Liste als ul, div oder eigene Komponente gerendert wird, solange die ARIA-Rollen konsistent gesetzt sind.
Ein häufiger Fehler ist, aria-expanded dauerhaft auf true zu setzen oder komplett wegzulassen, weil die Liste ohnehin per CSS ein- und ausgeblendet wird. Screenreader verlassen sich jedoch auf den ARIA-Zustand, nicht auf die visuelle Sichtbarkeit, weshalb beide synchron gehalten werden müssen.
<label for="site-search">Suche</label>
<input
type="text"
id="site-search"
role="combobox"
aria-expanded="false"
aria-controls="site-search-listbox"
aria-autocomplete="list"
autocomplete="off"
>
<ul id="site-search-listbox" role="listbox" hidden>
<li role="option" id="option-1">Herrenjacke Winter</li>
<li role="option" id="option-2">Herrenhose Cargo</li>
</ul>
3. aria-activedescendant statt Fokus-Verschiebung
Beim Navigieren durch die Vorschlagsliste mit den Pfeiltasten bleibt der tatsächliche Tastaturfokus im input-Element. Statt den Fokus auf jede einzelne Option zu verschieben, wird über aria-activedescendant auf dem input-Element mitgeteilt, welche Option gerade als aktiv gilt.
Dieses Muster hat einen entscheidenden Vorteil gegenüber echter Fokus-Verschiebung: Der Nutzer kann jederzeit weitertippen, ohne den Fokus erst wieder ins Eingabefeld zurückholen zu müssen. Bei einer echten Fokus-Verschiebung auf jede Option würde jeder Tastendruck den Fokus unerwartet zurück ins Feld springen lassen.
Die aktive Option muss zusätzlich visuell hervorgehoben werden, meist über eine eigene CSS-Klasse, die parallel zum aria-activedescendant-Wert per JavaScript gesetzt wird. Nur die Kombination aus visueller und programmatischer Markierung macht die Auswahl für sehende und blinde Nutzer gleichermaßen nachvollziehbar.
let activeIndex = -1;
const options = Array.from(listbox.querySelectorAll('[role="option"]'));
function setActiveOption(index) {
options.forEach((el) => el.classList.remove('bg-slate-100'));
activeIndex = index;
if (index >= 0) {
options[index].classList.add('bg-slate-100');
input.setAttribute('aria-activedescendant', options[index].id);
} else {
input.removeAttribute('aria-activedescendant');
}
}
4. Pfeiltasten-Navigation durch die Vorschläge implementieren
Die Tastatursteuerung folgt einem festen, dokumentierten Muster: Pfeil-runter bewegt die aktive Auswahl zur nächsten Option, Pfeil-hoch zur vorherigen, Enter übernimmt die aktive Option in das Eingabefeld, und Escape schließt die Liste, ohne eine Auswahl zu übernehmen.
Am unteren und oberen Ende der Liste sollte die Navigation entweder stoppen oder zum jeweils anderen Ende umspringen, konsistent mit dem Verhalten, das Screenreader-Nutzer aus nativen Auswahlfeldern kennen. Ein plötzliches Verschwinden der Auswahl am Listenende wirkt dagegen wie ein Programmfehler.
Die Tab-Taste sollte die Liste dabei niemals zum Navigieren verwenden, sondern ausschließlich zum Verlassen des gesamten Suchfelds dienen, wie bei jedem anderen Formularelement auch. Wird Tab für die Listennavigation zweckentfremdet, bricht das die Erwartungshaltung aller Tastaturnutzer, nicht nur die von Screenreader-Nutzern.
input.addEventListener('keydown', (event) => {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
setActiveOption(Math.min(activeIndex + 1, options.length - 1));
break;
case 'ArrowUp':
event.preventDefault();
setActiveOption(Math.max(activeIndex - 1, 0));
break;
case 'Enter':
if (activeIndex >= 0) {
input.value = options[activeIndex].textContent;
closeListbox();
}
break;
case 'Escape':
closeListbox();
break;
}
});
5. Screenreader-Ansage der Trefferanzahl
Neben der einzelnen Navigation durch Vorschläge ist auch die Gesamtzahl der Treffer eine wichtige Information, die sehende Nutzer visuell sofort erfassen, Screenreader-Nutzer aber aktiv mitgeteilt bekommen müssen. Ohne diese Information bleibt unklar, ob überhaupt Ergebnisse existieren oder ob das Laden noch läuft.
Eine separate, dezente Live-Region, unabhängig von der eigentlichen Vorschlagsliste, eignet sich dafür besser als eine Ansage direkt in der Listbox selbst. Eine Meldung wie „5 Treffer gefunden“ oder „Keine Treffer gefunden“ reicht als aria-live="polite"-Ansage vollständig aus.
Wichtig ist, diese Ansage erst nach Abschluss der Suche auszulösen, nicht bei jedem einzelnen Tastendruck während des Tippens. Ein serverseitiges Debounce von 200 bis 300 Millisekunden verhindert sowohl unnötige Serveranfragen als auch eine Flut von Zwischenansagen, die den eigentlichen Suchvorgang akustisch überlagern würden.
// Live-Region im Markup: <div aria-live="polite" class="sr-only" data-search-status></div>
function updateResultCount(count) {
const status = document.querySelector('[data-search-status]');
status.textContent = count === 0
? 'Keine Treffer gefunden.'
: `${count} Treffer gefunden.`;
}
6. Suchbegriff-Highlighting ohne Screenreader-Rauschen
Das visuelle Hervorheben des gesuchten Begriffs innerhalb der Vorschläge, meist über ein strong- oder mark-Element, ist für sehende Nutzer eine hilfreiche Orientierung. Screenreader lesen mark-Elemente standardmäßig ohne besondere Betonung vor, was in den meisten Fällen auch der richtige Umgang damit ist.
Problematisch wird es, wenn zusätzlich versucht wird, die Hervorhebung akustisch nachzubilden, etwa durch eingefügte Symbole oder wiederholte Ansagen des hervorgehobenen Textteils. Das führt zu unnötig langen, schwer verständlichen Ansagen und sollte vermieden werden.
Ein sinnvoller Kompromiss ist, das Highlighting rein visuell zu belassen und stattdessen die Gesamtzahl der Treffer sowie eine klare Struktur der einzelnen Optionen für die akustische Wahrnehmung zu optimieren, statt zu versuchen, jedes visuelle Detail eins zu eins akustisch nachzubilden.
7. Abgrenzung zu Datepicker- und klassischen Combobox-Patterns
Das Suchfeld-Pattern mit Live-Vorschlägen unterscheidet sich von klassischen Auswahl-Comboboxen und Datepickern vor allem darin, dass freier Text eingegeben werden kann, der nicht zwingend einem Vorschlag entsprechen muss. Bei einer klassischen Combobox oder einem Datepicker ist meist genau ein gültiger Wert aus einer festen Menge zu wählen.
Diese Unterscheidung wirkt sich direkt auf aria-autocomplete aus: Suchfelder mit freien Texteingaben verwenden meist aria-autocomplete="list", während strengere Auswahlfelder mit automatischer Vervollständigung eher aria-autocomplete="both" nutzen, was zusätzlich einen Inline-Vervollständigungsvorschlag im Eingabefeld selbst ermöglicht.
Ein eigener Artikel dieser Reihe behandelt barrierefreie Datepicker und klassische Comboboxen im Detail, inklusive deren spezifischer Tastatursteuerung für Datumsauswahl. Wer beide Pattern verwechselt und identisch implementiert, produziert für Nutzer verwirrende Erwartungsabweichungen, etwa wenn Enter bei einer Suche unerwartet ein Formular komplett absendet statt nur die Auswahl zu übernehmen.
8. Magento-Hyvä-Live-Search barrierefrei umsetzen
Die Hyvä-Live-Search-Komponente basiert typischerweise auf Alpine.js und einem debounced Fetch-Aufruf gegen die Magento-Such-API. Für eine barrierefreie Umsetzung müssen die ARIA-Attribute des Combobox-Patterns parallel zum bestehenden x-show/x-model-Zustand gepflegt werden, nicht als nachträglicher Zusatz.
Ein praktikabler Ansatz ist, die aria-expanded- und aria-activedescendant-Werte direkt aus denselben Alpine.js-Datenpunkten abzuleiten, die auch die sichtbare Darstellung steuern, statt eine parallele, potenziell inkonsistente zweite Zustandsverwaltung aufzubauen.
Die serverseitige Trefferanzahl aus der Magento-Such-API lässt sich direkt in die separate Live-Region für die Ansage übernehmen, ohne zusätzliche Logik im Frontend, da die API diese Zahl ohnehin für die Anzeige der Ergebnisse bereits mitliefert.
<div x-data="liveSearch()" class="relative">
<input
type="text"
role="combobox"
:aria-expanded="open ? 'true' : 'false'"
aria-controls="live-search-listbox"
:aria-activedescendant="activeId"
x-model="query"
@input.debounce.250ms="search()"
@keydown.arrow-down.prevent="moveActive(1)"
@keydown.arrow-up.prevent="moveActive(-1)"
@keydown.enter="selectActive()"
@keydown.escape="close()"
>
<ul id="live-search-listbox" role="listbox" x-show="open">
<template x-for="(result, index) in results" :key="result.id">
<li role="option" :id="'option-' + result.id" x-text="result.name"></li>
</template>
</ul>
<div aria-live="polite" class="sr-only" x-text="statusMessage"></div>
</div>
9. Häufige Fehler bei Autocomplete-Implementierungen
Der wohl verbreitetste Fehler ist, die Vorschlagsliste komplett ohne ARIA-Rollen als reines div mit Klick-Handlern zu bauen. Visuell funktioniert das einwandfrei, für Tastatur- und Screenreader-Nutzer bleibt die Liste jedoch faktisch unsichtbar und unbedienbar.
Ein zweiter häufiger Fehler ist, beim Öffnen der Liste den echten Fokus auf die erste Option zu verschieben, statt aria-activedescendant zu verwenden. Das unterbricht die Texteingabe sofort, da der Fokus das input-Element verlässt, sobald ein Vorschlag erscheint.
Ein dritter Fehler ist eine fehlende oder falsch synchronisierte aria-expanded-Angabe, die dazu führt, dass Screenreader eine Liste ankündigen, obwohl sie visuell längst geschlossen ist, oder umgekehrt eine geöffnete Liste komplett verschweigen. Beide Fälle wirken für Nutzer stark irritierend und untergraben das Vertrauen in die Ansagen des Screenreaders.
| ARIA-Attribut | Position | Zweck | Häufiger Fehler |
|---|---|---|---|
| role="combobox" | input-Element | Kennzeichnet das Feld als erweiterbare Eingabe | Fehlt komplett oder auf falschem Element gesetzt |
| aria-expanded | input-Element | Zeigt Sichtbarkeit der Vorschlagsliste an | Nicht synchron mit tatsächlicher CSS-Sichtbarkeit |
| aria-activedescendant | input-Element | Markiert aktuell aktive Option | Fehlt, Fokus wird stattdessen verschoben |
| role="listbox" | Container der Vorschläge | Kennzeichnet die Liste als Auswahlmenge | Container bleibt ohne Rolle als reines div |
| role="option" | Einzelner Vorschlag | Kennzeichnet einen wählbaren Eintrag | Fehlende eindeutige id für aria-activedescendant |
Mironsoft
WCAG-Audits, barrierefreie Magento-Shops und Schulungen
Unsicher, ob der Shop wirklich barrierefrei ist?
Wir prüfen bestehende Magento-Shops gegen WCAG 2.2, beheben konkrete Barrieren im Hyvä-Frontend und schulen Teams, damit Barrierefreiheit dauerhaft im Entwicklungsprozess verankert bleibt.
WCAG-Audit
Shop systematisch gegen WCAG 2.2 AA prüfen, mit priorisierter Fehlerliste.
Barrieren beheben
Konkrete Umsetzung: Tastaturbedienbarkeit, Screenreader-Support, Kontraste, Formulare.
Team-Schulung
Entwickler und Redakteure für barrierefreie Umsetzung im Alltag sensibilisieren.
10. Zusammenfassung
Suchfeld-Autocomplete
Grundstruktur
role="combobox" auf dem Eingabefeld, role="listbox" und role="option" für die Vorschlagsliste konsequent setzen.
Aktive Auswahl
aria-activedescendant statt echter Fokus-Verschiebung verwenden, damit die Texteingabe nicht unterbrochen wird.
Trefferanzahl
Ergebniszahl über eine separate, gedrosselte aria-live-Region ansagen, nicht bei jedem Tastendruck.
Abgrenzung
Freitext-Suchfelder mit aria-autocomplete="list" von strengeren Auswahl-Comboboxen mit "both" unterscheiden.