Zum Inhalt
wannacode

Barrierefreiheit

Kurz gesagt: Barrierefreiheit heißt, dass die Seite auch mit der Tastatur und einem Screenreader funktioniert: Der Browser baut aus dem HTML einen Baum mit Rolle, Name und Zustand jedes Elements.

Problem

Der Button, der kein Button ist

Die Produktseite funktioniert: Du klickst auf „In den Warenkorb“ und das Produkt landet im Warenkorb. Ein Kunde schreibt, dass er nichts kaufen kann. Er benutzt keine Maus: Er bewegt sich mit der Tastatur, und ein Screenreader sagt ihm, was auf der Seite steht.

HTML, CSS, JS

Klick in das Feld „Menge“ und drück Tab. Der Fokus überspringt „In den Warenkorb“ und springt zum Herz. Das Herz ist ein Button, aber der Screenreader sagt nur „Button“: Was er tut, bleibt unbekannt. Beide Elemente sehen gut aus. Der Fehler steckt in dem, was der Browser nicht zeichnet.

Die Idee

Der zweite Baum

Aus dem DOM baut der Browser noch einen Baum: den Barrierefreiheitsbaum. Er ist die Seite, wie sie die Menschen und Programme sehen, die nicht auf den Bildschirm schauen:

  • ein Screenreader liest einem Menschen, der nicht sehen kann, die Seite vor;
  • Sprachsteuerung: Jemand sagt „Klick auf In den Warenkorb“, und das Programm sucht einen Button mit diesem Namen;
  • die Tastatur: Tab geht durch die Elemente, die man auslösen kann;
  • automatische Tests wie die Testing Library suchen nach Rolle und Name. Die Sandbox hier prüft auch so.

Jeder Knoten dieses Baums hat drei Dinge:

  • eine Rolle: was das Element ist: Button, Link, Überschrift, Feld, Bild;
  • einen zugänglichen Namen: wie es heißt: „In den Warenkorb“;
  • einen Zustand: ob es gedrückt, markiert oder deaktiviert ist.

Die Rolle kommt vom Tag: <button> ist ein Button, <a href> ist ein Link, <h1> ist eine Überschrift. <div> und <span> haben keine Rolle. Im Baum bleibt nur ihr Text.

Sieh es dir an

Sieh dir die zwei Bäume an

Eine Produktkarte mit drei „Buttons“. Achte rechts darauf, was von jedem übrig bleibt.

<body>
<article>
<h2>Brot</h2>
<img src="bread.svg" alt="Brot">
<label>Menge <input type="number"></label>
<div onclick="add()">Hinzufügen</div>
<button><svg>…</svg></button>
<button>Hinzufügen</button>
</article>
</body>
Schritt für Schritt, in Worten
  1. Links steht das DOM. Daraus baut der Browser einen zweiten Baum: den Barrierefreiheitsbaum. Ihn lesen Screenreader, Sprachsteuerung und Tests nach Rolle. <article> wird zur Rolle article.
  2. <h2> wird zu heading (Überschrift). Sein Name kommt vom Text darin: „Brot“.
  3. Ein Bild hat keinen Text darin. Sein Name kommt aus alt. Ohne alt hat das Bild keinen Namen.
  4. Das Feld bekommt die Rolle spinbutton (ein Zahlenfeld), und sein Name kommt aus <label>: „Menge“. <label> selbst steht nicht im Baum, nur sein Text.
  5. <div> hat keine Rolle. onclick ändert daran nichts. Übrig bleibt nur der Text „Hinzufügen“. Er ist kein Button, und die Tastatur erreicht ihn nicht.
  6. Das ist ein echter Button, aber darin steht nur ein Icon ohne Text. Der Name ist leer. Ein Screenreader sagt nur „Button“.
  7. Ein <button> mit Text: die Rolle button und der Name „Hinzufügen“. Tab erreicht ihn, und Enter und Leertaste lösen ihn aus.
  8. getByRole('button', { name: 'Hinzufügen' }) sucht im rechten Baum. Es findet einen Button: den echten. Das <div> und der Button ohne Namen existieren für diese Suche nicht.

Den Barrierefreiheitsbaum kannst du im Browser sehen: Öffne in den Entwicklertools (F12) „Elemente“ und das Panel Barrierefreiheit. Es zeigt Rolle und Namen des ausgewählten Elements.

Die Idee

Woher der Name kommt

Den Namen eines Elements bestimmen feste Regeln. Die häufigsten Quellen:

  • der Text darin: <button>In den Warenkorb</button>, <a href="/cart">Warenkorb</a>, <h1>;
  • das alt eines Bildes: <img src="logo.svg" alt="Der Shop">. Ein Link, in dem nur ein Bild steht, bekommt seinen Namen aus alt;
  • das <label> eines Feldes: „Menge“ ist der Name des Feldes daneben;
  • aria-label: ein Name, der direkt im Attribut steht, wenn es keinen sichtbaren Text gibt: <button aria-label="Zu den Favoriten hinzufügen"> mit einem Icon darin;
  • aria-labelledby: Der Name ist der Text eines anderen Elements, das per id angegeben wird.

aria-label und aria-labelledby sind stärker als der Text darin. Der Name sagt, was das Element tut, nicht wie es aussieht: „In den Warenkorb“, nicht „Einkaufswagen“. Bei einem Bild in einem Link sagt der Name, wohin der Link führt: „Der Shop“, nicht „Logo“.

Probier es aus

Gib ihnen Namen

Das Logo oben führt zur Startseite, ist aber ein Bild ohne alt. Der Warenkorb-Button ist nur ein Icon. Gib dem Link einen Namen („Der Shop“) und dem Button einen Namen („In den Warenkorb“). Die Prüfung findet sie nach Rolle und Name und drückt den Button.

HTML, CSS, JS

Die Idee

Die Tastatur und der Fokus

Nicht alle benutzen eine Maus. Manche haben zitternde Hände, manche sehen den Zeiger nicht, manche tippen mit der Tastatur schneller. Sie bewegen sich mit Tab durch die Seite und lösen mit Enter aus.

Der Fokus ist das Element, das gerade die Tasten bekommt. Tab schiebt ihn vor, Umschalt+Tab zurück. Tab erreicht nur Elemente, die man auslösen kann: Links mit href, Buttons, Felder, Dropdowns. Die Reihenfolge ist die DOM-Reihenfolge, von oben nach unten.

<button> bringt alles mit: eine Rolle, Fokus, und Enter und Leertaste lösen es aus. <div> hat nichts davon. Ihm Fokus und Enter beizubringen heißt, noch einmal <button> zu schreiben.

tabindex ändert, welches Element den Fokus bekommt:

  • tabindex="0": Das Element reiht sich in die Tab-Reihenfolge ein, an seiner Stelle im DOM;
  • tabindex="-1": Tab überspringt es, aber Code kann es mit focus() fokussieren;
  • tabindex="1" und höher: Das Element kommt zuerst, vor allen anderen. Die Reihenfolge der Seite geht kaputt. Benutz das nicht.

Der Browser zeichnet einen Rahmen um das fokussierte Element. Ohne ihn weiß ein Tastaturnutzer nicht, wo er ist. Entferne ihn nicht. Wenn er dir nicht gefällt, ersetz ihn durch einen anderen.

Probier es aus

Mit der Tastatur

„In den Warenkorb“ ist ein <div>. Sorg dafür, dass Tab es nach dem Feld „Menge“ erreicht und dass Enter in den Warenkorb legt. Die Prüfung drückt zweimal Tab und dann Enter.

HTML, CSS, JS

Probier es aus

Finde den Fehler

Der Button funktioniert mit der Maus. Ein Tastaturnutzer drückt Tab und sieht nicht, wo er ist. Welche Zeile ist schuld?

Wähle die Zeile mit dem Fehler.

Die Idee

Überschriften und die erste Regel von ARIA

Überschriften sind das Inhaltsverzeichnis der Seite. Ein Screenreader-Nutzer liest die Seite nicht von vorn. Er bittet um eine Liste der Überschriften oder springt mit einer Taste zwischen ihnen (H in NVDA und JAWS). Deshalb erzählen die Ebenen die Gliederung: ein <h1> für die Seite, <h2> für die Abschnitte, <h3> darin. Wähl nicht <h4>, nur weil es kleiner ist. Die Größe änderst du mit CSS.

ARIA (Accessible Rich Internet Applications) ist eine Menge von Attributen, die den Barrierefreiheitsbaum ändern: role, aria-label, aria-expanded, aria-live und andere. Sie sagen dem Screenreader etwas, das HTML allein nicht sagen kann.

ARIA ändert nur, was angesagt wird, nicht das Verhalten. role="button" auf einem <div> lässt den Screenreader „Button“ sagen, aber Tab erreicht es trotzdem nicht und Enter tut trotzdem nichts. Die Person hört „Button“, drückt, und nichts passiert. Das ist schlimmer als ein <div> ohne Rolle.

Deshalb lautet die erste Regel von ARIA: Wenn es ein HTML-Element gibt, das das kann, nimm es. <button>, nicht role="button". <a href>, nicht role="link". <nav>, nicht role="navigation". ARIA ist für die Fälle, in denen HTML kein solches Element hat oder es keinen sichtbaren Text gibt, aus dem ein Name kommen könnte.

Probier es aus

Ergänze

Welches Attribut gibt einem Button, der nur ein Icon hat, einen Namen?

<button type="button" ="Zu den Favoriten hinzufügen">
  <svg viewBox="0 0 24 24">…</svg>
</button>
Genauer, für Neugierige

Meldungen, die erscheinen. Nach „In den Warenkorb“ schreibt die Seite „Hinzugefügt: Dobrudzha-Brot“. Wer auf den Bildschirm schaut, sieht es. Der Screenreader bemerkt die Änderung nicht, weil der Fokus nicht dort ist. <p role="status"> (oder aria-live="polite") sagt: Wenn sich der Text hier ändert, lies ihn vor, sobald du mit dem Aktuellen fertig bist. Für dringende Fehler gibt es role="alert", das unterbricht. Das Element muss vor der Meldung auf der Seite sein: Nur sein Text ändert sich.

Die Sprache. <html lang="de"> sagt dem Screenreader, mit welcher Stimme er lesen soll. Bei falschem lang wird deutscher Text mit englischer Aussprache gelesen und ist nicht zu verstehen. Ein Wort in einer anderen Sprache im Text bekommt sein eigenes: <span lang="fr">Croissant</span>.

Verstecken. display: none und das Attribut hidden entfernen das Element vom Bildschirm und aus dem Barrierefreiheitsbaum. aria-hidden="true" entfernt es nur aus dem Baum: für Icons neben Text, die der Screenreader nicht sagen muss. Ein fokussierbares Element in aria-hidden ist ein Fehler: Tab erreicht es, und der Screenreader schweigt.

Die Reihenfolge der Namen. Den vollständigen Algorithmus beschreibt der Standard Accessible Name and Description Computation. Kurz: erst aria-labelledby, dann aria-label, dann das, was HTML liefert (<label>, alt, der Text darin), zuletzt title. title und placeholder sind schlechte Namen: title erscheint nur, wenn du mit der Maus darüberfährst, und placeholder verschwindet, sobald du tippst.

:focus und :focus-visible. :focus gilt immer, wenn das Element den Fokus hat, auch nach einem Mausklick. :focus-visible gilt, wenn der Browser findet, dass der Rahmen sichtbar sein soll: bei Tab, aber nicht nach einem Klick auf einen Button. Wenn du den Rahmen ändern willst, nimm also :focus-visible. CSS ist das nächste Modul.

Zum Inhalt springen. Der erste Link auf vielen Seiten ist „Zum Inhalt springen“ (<a href="#main">). Er bleibt versteckt, bis er den Fokus bekommt. So muss ein Tastaturnutzer nicht jedes Mal das ganze Menü durchgehen.

Übung

Der Warenkorb

Der Warenkorb wurde in Eile gebaut. „Warenkorb“ ist nur fetter Text, die „ד-Buttons sind <span>, und der Link „Zur Kasse“ hat tabindex="1", „damit er der erste ist“. Bring es in Ordnung: eine Überschrift der Ebene 1; Buttons, die Tab erreicht und Enter auslöst, mit den Namen „Entfernen Dobrudzha-Brot“ und „Entfernen Frischmilch 3%“; Tab in der Reihenfolge der Seite: der erste Button, der zweite, der Link. Die Prüfung geht nur mit der Tastatur durch den Warenkorb.

HTML, CSS, JS

Test

Teste dich

1. Was fehlt <div onclick="add()">Hinzufügen</div> im Vergleich zu <button>?
2. Wie lautet der zugängliche Name von <button type="button" aria-label="Schließen">×</button>?
3. Der Link „Zur Kasse“ hat tabindex="3". Was passiert?
4. Was bewirkt role="button" auf einem <div>?
5. Der Untertitel des Produkts soll kleiner sein. Was ist richtig?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·