WCAG-konforme Umsetzung am Beispiel eines Number Inputs
Autor
Julian Bennecker
Senior Software Developer
bei SYZYGY Techsolutions
Lesedauer
8 Minuten
Publiziert
27.02.2025

Am 28. Juni 2025 tritt das Barrierefreiheitsstärkungsgesetz (BFSG) in Kraft. Dieses Gesetz dient der Umsetzung des European Accessibility Acts (EAA) und schreibt vor, dass digitale Produkte und Dienstleistungen bestimmter Unternehmen barrierefrei gestaltet sein müssen. In diesem Artikel zeige ich anhand eines bestehenden Number Inputs, das wir barrierefrei machen sollten, wie komplex die barrierefreie Umsetzung eines scheinbar simplen UI-Elements sein kann. Viele Elemente wirken auf den ersten Blick einfach, doch ihre barrierefreie Umsetzung gemäß den Web Content Accessibility Guidelines (WCAG) stellt oft eine Herausforderung dar.

Was ist ein Number Input?
Ein Number Input ist ein Eingabeelement für Zahlen. Nutzer:innen können den Wert über Plus- und Minus-Schaltflächen anpassen oder den gewünschten Wert direkt über die Tastatur eingeben. Solche Inputs werden häufig in Formularen oder Konfigurationsdialogen genutzt.

Ausgangspunkt: Das alte Number Input
Im Juli 2021 wurde das Barrierefreiheitsstärkungsgesetz (BFSG) erlassen, das den European Accessibility Act (EAA) ins nationale Recht überführt. Es definiert Barrierefreiheitsanforderungen für Produkte und Dienstleistungen und tritt am 28.06.2025 in Kraft.
In einem unserer Projekte wurde hierfür kürzlich ein Audit zur Überprüfung der Barrierefreiheit durchgeführt. Dabei fiel auf, dass das Number Input nicht barrierefrei war. Die ursprüngliche Umsetzung legte den Fokus hauptsächlich auf die optische Darstellung, weniger auf die semantische Struktur. So ungefähr sah der ursprüngliche Code aus:
export function NumberInput() {
const [value, setValue] = useState(0)
return (
<div className="number-input">
<div>Label</div>
<div>Beschreibungstext</div>
<div>
<button
aria-label="Wert verringern"
disabled={value <= minValue}
onClick={() => handleClick('decrement')}
>
-
</button>
<div className="value">{value}</div>
<button
aria-label="Wert erhöhen"
disabled={value >= maxValue}
onClick={() => handleClick('increment')}
>
+
</button>
</div>
</div>
)
}
Obwohl die Buttons bereits mit `aria-labels` versehen wurden, um sicherzustellen, dass die Symbole für Plus und Minus von Screenreadern richtig interpretiert werden, hat das Audit ergeben, dass die Nummerneingabe nicht den Anforderungen für Barrierefreiheit entspricht.
Die Probleme
- Menschen mit Sehbehinderungen wurden nicht über Wertänderungen informiert (WCAG-Kriterium: 4.1.3 Status Updates). Sie verwenden in der Regel Screenreader (auch Vorleseprogramme genannt). Das sind Programme, die den Inhalt des Bildschirms vorlesen und über die Tastatur gesteuert werden.
- Die Beschriftung und der Beschreibungstext waren technisch nicht mit dem Eingabefeld verbunden. Sie nicht nur visuell, sondern auch technisch zu verbinden, hat einige Vorteile. Zum Beispiel lesen Screenreader die Beschriftung und den Beschreibungstext vor, wenn die Nutzer:innen das Eingabefeld fokussieren, was ihnen hilft zu verstehen, was sie eingeben sollen. Ein weiterer Vorteil ist, dass wenn man auf die Beschreibung klickt, das Eingabefeld fokussiert wird. Dieser vergrößerte Klickbereich erleichtert das Aktivieren des Feldes – auch bei Touch-Geräten.
- Wenn Nutzer:innen den minimalen oder maximalen Wert erreicht haben, wurde der Button über das Attribut `disabled`. Dies hat zur Folge, dass der Button kein interaktives Element mehr ist und somit nicht mehr fokussiert werden kann. Der Fokus lag also nicht mehr auf dem Button und Menschen mit Sehbehinderungen wurden nicht über den Grund informiert, warum keine Interaktion mehr möglich ist.
- Eingaben per Tastatur wurden nur teilweise unterstützt. Die Up-/Down-Tasten funktionierten nicht, und eine Direkteingabe war nicht möglich. Eine barrierefreie Anwendung muss jedoch vollständig per Tastatur bedienbar sein.

Die neue, barrierefreie Lösung
Um diese Probleme zu lösen, wurden mehrere Maßnahmen umgesetzt:
1. Ansagen der Wertänderungen
Ein Kernproblem war, dass Screenreader-Nutzer:innen keine Rückmeldung über Wertänderungen erhielten. Die Lösung: ein visuell verstecktes Element mit `aria-live=”polite”`-Attribut. Dieses Element sorgt dafür, das Screenreader die Wertänderung ansagen.
export function NumberInput() {
const [value, setValue] = useState(0)
const [assertiveValue, setAssertiveValue] = useState<number | null>(null)
useEffect(() => {
if (assertiveValue === null) return
const timeout = setTimeout(() => setAssertiveValue(null), 2000)
return () => clearTimeout(timeout)
}, [assertiveValue])
return (
<div className="number-input">
<div>Label text</div>
<div>Beschreibungstext</div>
<div>
<button
aria-label="Wert verringern"
disabled={value <= minValue}
onClick={() => handleClick('decrement')}
>
-
</button>
<div className="value">{value}</div>
<button
aria-label="Wert erhöhen"
disabled={value >= maxValue}
onClick={() => handleClick('increment')}
>
+
</button>
<div className="sr-only" aria-live="polite">
{assertiveValue}
</div>
</div>
</div>
)
}
2. Verbesserte Tastatursteuerung und Semantik
Das `div` zur Anzeige des Wertes wurde durch ein natives `input[type=”number”]` ersetzt. Jetzt können Nutzer:innen mit den Pfeiltasten den Wert verändern oder den gewünschten Wert direkt eingeben. Dies ist besonders wichtig, da eine barrierefreie Anwendung vollständig über die Tastatur bedienbar sein muss. Auf mobilen Geräten öffnet sich so außerdem automatisch die numerische Tastatur.

3. Semantisches Label und Beschriftung
Das `div` für die Beschriftung wurde durch ein `label` ersetzt, um sicherzustellen, dass Screenreader die Verbindung zwischen Beschriftung und Eingabefeld korrekt herstellen. Zusätzlich wird `aria-describedby` verwendet, um das Eingabefeld mit dem Beschreibungstext zu verknüpfen. Dies stellt sicher, dass Screenreader-Nutzer:innen den zusätzlichen Kontext direkt mitgeteilt bekommen und so eine bessere Orientierung erhalten.
<label htmlFor="stepper-id">Label text</label>
<div id="description">Beschreibungstext</div>
<input
id="stepper-id"
type="number"
aria-describedby="description"
min={minValue}
max={maxValue}
value={value}
/>
4. Buttons: `aria-disabled` statt `disabled`
Statt `disabled` verwenden wir `aria-disabled`. Dies hat den Vorteil, dass die Schaltflächen fokussierbar bleiben und Nutzer:innen mit Screenreadern eine Rückmeldung erhalten, wenn sie nicht mehr gedrückt werden können. Andernfalls würde der Fokus verloren gehen und es wäre unklar, warum keine Interaktion mehr möglich ist.
<button
aria-label="Wert verringern"
aria-disabled={value === minValue}
onClick={() => handleClick('decrement')}
>
-
</button>
Fazit
Mit diesem scheinbar einfachen Beispiel zeige ich, wie viele Änderungen notwendig sind, um selbst grundlegende UI-Elemente barrierefrei umzusetzen. Überträgt man dies auf einen Komponenten-Katalog aktueller und moderner Webseiten, wird deutlich, dass es alles andere als trivial ist, Webseiten oder Digital Experience Platforms barrierefrei zu gestalten und zu implementieren. Das gilt erst recht, wenn bestehende Seiten nachträglich barrierefrei gemacht werden müssen.
Zudem wird klar, dass diese Anforderungen an Accessibility auch die Art und Weise verändern, wie Projektbeteiligte, z.B. Entwickler:innen denken und arbeiten müssen: Wie können wir Barrierefreiheit nicht als „nachträglichen Fix“, sondern als integralen Bestandteil unserer Arbeit betrachten? Wie schaffen wir Bewusstsein bei allen Beteiligten? Wie testen und entwickeln wir? Welche Werkzeuge gibt es, die uns dabei unterstützen können?
Diese Fragen werden immer relevanter – und genau darum geht es im nächsten Beitrag! Stay tuned!

Head of Technology