Barrierefreiheit

Warum Barrierefreiheit?

Barrierefreiheit im Web (Auch bekannt unter dem Begriff a11y) beschreibt Design und Umsetzung von Websites welche ausnahmlos von Jedem genutzt werden können. Barriefreiheit ist notwendig um Assistenzprogrammen das Interpretieren von Websites zu ermöglichen.

React unterstützt das Erstellen von barrierefreien Webseiten in vollem Ausmaß, unter anderem durch die Nutzung von Standard-HTML-Technologien.

Standards und Anleitungen

WCAG

Die Web Content Accessibility Guidelines enthalten Anleitungen um barrierefreie Webseiten zu erstellen.

Die folgenden WCAG-Checklisten geben einen kurzen Überblick:

WAI-ARIA

Das Web Accessibility Initiative - Accessible Rich Internet Applications Dokument zeigt Techniken für das Entwickeln vollkommen barrierefreier JavaScript Widgets.

Es gilt, dass alle aria-* HTML-Attribute in JSX komplett unterstützt werden. Während die meisten DOM-Eigenschaften und Attribute in React in camelCase umgesetzt werden, sollten diese Attribute hyphen-cased (auch bekannt als kebab-case, lisp-case, usw.) sein, so wie man es auch bei normalem HTML tun würde:

<input
  type="text"
  aria-label={labelText}  aria-required="true"  onChange={onchangeHandler}
  value={inputValue}
  name="name"
/>

Semantisches HTML

Semantisches HTML ist das Fundament der Barrierefreiheit einer Webanwendung. Das Nutzen der verschiedenen HTML-Elemente, welche die Bedeutung einer Information betonen bringt dir oftmals Barrierefreiheit ohne extra Aufwand.

Manchmal wird die Bedeutung verschiedener HTML Elemente verfälscht indem ein <div> Element um das JSX hinzugefügt wird um den React-Code zum laufen zu bringen, besonders häufig beim Arbeiten mit Listen (<ol>, <ul> und <dl>) und der HTML <table>. Hier sollten besser React Fragments genutzt werden um mehrere Elemente in einer Gruppe zusammenzufassen.

Zum Beispiel,

import React, { Fragment } from 'react';
function ListItem({ item }) {
  return (
    <Fragment>      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </Fragment>  );
}

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        <ListItem item={item} key={item.id} />
      ))}
    </dl>
  );
}

Es ist auch möglich mehrere Fragmente in einem Array mit Hilfe der map-Funktion zusammenzufassen, genauso wie du es bei anderen Element auch tun würdest:

function Glossary(props) {
  return (
    <dl>
      {props.items.map(item => (
        // Fragments should also have a `key` prop when mapping collections
        <Fragment key={item.id}>          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </Fragment>      ))}
    </dl>
  );
}

Solltest du keine Props für einen Fragment-Tag brauchen kannst du die folgende short syntax nutzen, sofern dein JS-Tooling diese unterstützt:

function ListItem({ item }) {
  return (
    <>      <dt>{item.term}</dt>
      <dd>{item.description}</dd>
    </>  );
}

Weitere Infos findest in der Dokumentation über Fragments.

Barrierefreie Formulare

Kennzeichnung

Jedes HTML Formularelement, wie zum Beispiel <input> und <textarea>, muss barrierefrei gekennzeichnet werden. Es müssen beschreibende Kennzeichnungen, welche für Screenreader relevant sind, vorhanden sein.

In folgenden Quellen kannst du nachlesen wie dies am besten umzusetzen ist:

Auch wenn diese HTML-Standards direkt in React genutzt werden können, solltest du darauf achten dass das for-Attribut in JSX htmlFor heißt.

<label htmlFor="namedInput">Name:</label><input id="namedInput" type="text" name="name"/>

Den Nutzer über Fehler informieren

Fehlersituation müssen von allen Nutzern gleich verstanden werden. Die folgenden Links zeigen uns wie man Fehlermeldungen auch für Screenreader interpretierbar gestaltet:

Fokuskontrolle

Stelle sicher dass deine Webanwendung vollständig per Tastatur bedienbar ist:

Tastatur-Fokus und Fokussierung

Der Tastatur-Fokus zeigt auf das aktuell ausgewählte DOM-Element welches Eingaben der Tastatur annimmt. Man sieht dies überall anhand der Fokussierung (Außenlinie), genau wie diese im folgenden Bild:

Blue keyboard focus outline around a selected link.

Verwende nur dann CSS zum Entfernen dieser Linie, zum Beispiel indem du outline: 0 setzt, wenn du sie durch eine andere Implementierung der Fokus-Markierung ersetzt.

Mechanismen um direkt zum wichtigsten Inhalt zu springen

Stelle einen Mechanismus zur Verfügung welcher die Navigationselemente deiner Webanwendung überspringt. Dies beschleunigt die Navigation mit der Tastatur.

Skiplinks, oder auch Skip-Navigation-Links sind versteckte Navigations-Links die nur dann sichtbar werden wenn Tastaturnutzer mit der Seite interagieren. Diese sind sehr einfach mit internen Seitenankern und etwas Styling umzusetzen:

Also use landmark elements and roles, such as <main> and <aside>, to demarcate page regions as assistive technology allow the user to quickly navigate to these sections. Nutze Außerdem semantische Elemente und Rollen, wie zum Beispiel <main>und <aside> um verschiedene Bereiche deiner Seite voneinander abzugrenzen, da es entprechende Assitenztechnologien so dem User erlauben sich schnell zwischen diesen Elementen zurecht zu finden.

Mehr über den Einsatz dieser Elemente zur Verbesserung der Barrierefreiheit erfährst du hier:

Den Fokus programmatisch verwalten

Unsere React-Anwendungen verändern das HTML-DOM kontinuierlich zur Laufzeit, dies führt dazu, dass der Tastatur-Fokus gelegentlich verloren geht oder auf ein unvorhersehbares Element gesetzt wird. Um diesem Umstand zuvor zu kommen, müssen wir hier programmatisch nachhelfen. Zum Beispiel indem wir den Tastatur-Fokus wieder auf den Button setzen, welcher ein Modal geöffnet hat, nachdem dieses geschlossen wurde.

MDN Web Docs takes a look at this and describes how we can build keyboard-navigable JavaScript widgets. Die MDN Web Docs beschreiben wie wir Tastaturgesteurte JavaScript Widgets realisieren können.

Um mit React einen Fokus zu setzen, können wir Refs auf DOM-Elementen nutzen.

Hierzu erstellen wir zuerst eine Elementreferenz im JSX einer Component-Klasse:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // Create a ref to store the textInput DOM element    this.textInput = React.createRef();  }
  render() {
  // Use the `ref` callback to store a reference to the text input DOM  // element in an instance field (for example, this.textInput).    return (
      <input
        type="text"
        ref={this.textInput}      />
    );
  }
}

Danach können wir den Fokus beliebig verändern, falls nötig:

focus() {
  // Explicitly focus the text input using the raw DOM API
  // Note: we're accessing "current" to get the DOM node
  this.textInput.current.focus();
}

Gelegentlich ist es nötig, dass eine Eltern-Komponente den Fokus auf ein Element einer Kind-Komponente setzen muss. Dies lässt sich mit dem Bereitstellen von DOM-Referenzen einer Eltern-Komponente mit einer speziellen Eigenschaft, welche die Referenz der Eltern-Komponente zur Kind-Komponente weitergibt, umsetzen.

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />    </div>
  );
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();  }
  render() {
    return (
      <CustomTextInput inputRef={this.inputElement} />    );
  }
}

// Now you can set focus when required.
this.inputElement.current.focus();

Falls du einen Higher-Order-Component nutzt empfiehtl es sich die Referenz zum umschlossenen Component weiterzugeben indem du die forwardRef React-Funktion nutzt. Sollte ein Third-Party-HOC diese Technik nicht implentiert haben kannst du das oben verwendete Pattern als einen Fallback nutzen.

Ein gutes Fokus-Beispielt ist das react-aria-modal. Dies ist ein relativ rares Beispiel eines vollständig barrierefreiem Modal-Fenster. Hier wird der initiale Fokus auf den Abbrechen Button gesetzt (Um den User davon abzuhalten versehentlich die Success-Action zu aktivieren) und der Fokus kann nicht aus dem Modal-Fenster springen. Nachdem das Modal geschlossen wird wird der Fokus wieder zurück auf das Element gesetzt welches das Modal-Fenster getriggered hat.

Hinweis:

Es handelt sich hier um ein wichtiges Feature, welches jedoch in einem vernünftigen Rahmen genutzt werden sollte. Nutze dieses Feature, um den Tastatur-Fokus anzupassen, falls der Flow der App gestört sein sollte und versuche nicht, die Tastaturnutzung des Users zu antizipieren.

Maus- und Pointerevents

Stelle sicher, dass alle Funktionalitäten, welche durch die Maus oder den Zeiger hervorgerufen werden, auch via Tastatur gesteuert werden können. Sich komplett von einem Zeigergerät abhängig zu machen, führt dazu, dass deine App für viele Tastaturnutzer unbrauchbar wird.

Um dies zu verdeutlichen, schauen wir uns ein Anwendungsbeispiel an, bei dem die Barrierefreiheit durch Click-Events gestört ist. Hier siehst du das “outside-click”-Pattern, in welchem der User eine geöffnete Popup-Nachricht schließen kann, indem er außerhalb des Elements klickt.

Ein Togglebutton welcher eine Popoverliste öffnet, die zeigt dass diese per Klick außerhalb des Elements zu schließen ist.

Typischerweise wird Dies durch ein Klick-Event auf dem window-Objekt implementiert welches die Popover-Nachricht schließt:

class OuterClickExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
    this.toggleContainer = React.createRef();

    this.onClickHandler = this.onClickHandler.bind(this);
    this.onClickOutsideHandler = this.onClickOutsideHandler.bind(this);
  }

  componentDidMount() {    window.addEventListener('click', this.onClickOutsideHandler);  }
  componentWillUnmount() {
    window.removeEventListener('click', this.onClickOutsideHandler);
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }));
  }

  onClickOutsideHandler(event) {    if (this.state.isOpen && !this.toggleContainer.current.contains(event.target)) {      this.setState({ isOpen: false });    }  }
  render() {
    return (
      <div ref={this.toggleContainer}>
        <button onClick={this.onClickHandler}>Select an option</button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
}

Dies würde natürlich mit Zeigergeräten, wie zB. der Maus, super funktionieren, mit der Tastatur alleine führt dies jedoch zu mangelhafter Funktionalität wenn du das nächste Element per Tab erreichst. In diesem Fall das click-Event nie auf dem window-Objekt aufgerufen. Dies kann zu obskuren Fehlern führen, welche es manchen Usern unmöglich macht deine App zu nutzen.

Ein Togglebutton welcher eine Popoverliste öffnet, die nur per Click-Event wieder zu schließen ist.

Die gleiche Funktionalität kann durch geeignete Eventhandler, wie zum Beispiel onBLur und onFocus, erreicht werden:

class BlurExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isOpen: false };
    this.timeOutId = null;

    this.onClickHandler = this.onClickHandler.bind(this);
    this.onBlurHandler = this.onBlurHandler.bind(this);
    this.onFocusHandler = this.onFocusHandler.bind(this);
  }

  onClickHandler() {
    this.setState(currentState => ({
      isOpen: !currentState.isOpen
    }));
  }

  // We close the popover on the next tick by using setTimeout.  // This is necessary because we need to first check if  // another child of the element has received focus as  // the blur event fires prior to the new focus event.  onBlurHandler() {    this.timeOutId = setTimeout(() => {      this.setState({        isOpen: false      });    });  }
  // If a child receives focus, do not close the popover.  onFocusHandler() {    clearTimeout(this.timeOutId);  }
  render() {
    // React assists us by bubbling the blur and    // focus events to the parent.    return (
      <div onBlur={this.onBlurHandler}           onFocus={this.onFocusHandler}>        <button onClick={this.onClickHandler}
                aria-haspopup="true"
                aria-expanded={this.state.isOpen}>
          Select an option
        </button>
        {this.state.isOpen && (
          <ul>
            <li>Option 1</li>
            <li>Option 2</li>
            <li>Option 3</li>
          </ul>
        )}
      </div>
    );
  }
}

Der obige Code gewährleistet die Funktionalität sowohl für Zeigergeräte als auch für Tastaturen. Beachte auch die aria-* Attribute zur Unterstützung von Screenreadern. Der Einfachheit halber wurden hier keine Pfeiltasten-Events implementiert um mit dem Popover zu interagieren.

Eine Popoverliste, welche sowohl per Maus als auch per Tastatur nutzbar ist.

Dies hier ist nur ein Beispiel für viele Fälle bei denen die Funktionalität nicht gewährleistet ist wenn du dich nur auf Zeiger-Events verlässt. Ein beständiges Testen mit der Tastutur zeigt dir auch die Schwachstellen, welche mit angemessenen Eventhandlern beseitigt werden können.

Kompliziertere Widgets

Kompliziertere Handlungen sollten nicht mit weniger Barrierefreiheit einhergehen. Während Barrierefreiheit am einfachen damit umgesetzt werden kann sich so gut wie möglich an den HTML-Standard zu halten, können auch die kompliziertesten Widgets barrierefrei implementiert werden.

Here we require knowledge of ARIA Roles as well as ARIA States and Properties. Wir setzen hier Grundkentinsse in den ARIA Rollen sowie den ARIA Zuständen und Eigenschaften hervor.

Jede Art von Widget hat ein spezielles Designpattern und sollte für User und deren Useragents wie folgt funktionieren:

Andere wichtige Punkte

Einstellung der Sprache

Gib immer die in den Texten deiner Seite verwendete Sprache an, da Screen-Reader-Software diese Angabe nutzt um die richtigen Spracheinstellungen auszuwählen.

Der Titel des Dokuments

Verwende den Dokumenten <title> um den aktuellen Inhalt der Seite korrekt zu beschreiben, da dies sicherstellt, dass der Benutzer über den aktuellen Seitenkontext informiert ist.

In React können wir Diesen mit dem React Document Title Component realisieren.

Farbkontraste

Stelle sicher, dass die Texte deiner Webseite ausreichende Farbkontraste aufweisen, um auch von Nutzern mit Sehschwäche problemlos lesbar zu sein:

Es kann sehr anstrengend sein die richtigen Farbkombinationen für alle Anwendungsfälle deiner Webseite manuell zu erstellen, anstatt dessen kannst du eine vollständig barrierefreie Farbpalette mit Colorable berechnen.

Sowohl die unten erwähnten aXe als auch die WAVE-Werkzeuge beinhalten Farbkontrast-Tests welche dich über fehlerhafte Kontraste informieren.

Wenn du deine Kontraste noch genauer testen willst kannst du die folgenden Werkzeuge verwenden:

Entwicklungs- und Testingtools

Es gibt eine Reihe von Werkzeugen um dich bei der Erstellung barrierefreier Webanwendungen zu unterstützen.

Die Tastatur

Die mit Abstand einfachste und auch eine der wichtigsten Prüfungen ist es, zu testen, ob deine gesamte Webseite allein mit der Tastatur erreichbar und nutzbar ist. Teste dies wie folgt:

  1. Deaktiviere deine Maus.
  2. Nutze Tab und Shift+Tab um zu navigieren.
  3. Nutze Enter um Elemente zu aktivieren.
  4. Falls nötig, nutze die Pfeiltasten zur Interaktion mit manchen Elementen, wie z.B. Menüs und Dropdowns.

Hilfsmittel für Entwickler

Einige Punkte der Barrierefreiheit können direkt in unserem JSX-Code getestet werden. Oftmals gibt es bereits Intellisense-Tests für ARIA-Roles, States und Properties in JSX-fähigen IDE’s. Außerdem steht uns das folgende Werkzeug zur Verfügung:

eslint-plugin-jsx-a11y

Das eslint-plugin-jsx-a11y ESLint-Plugin stellt AST-Linting-Feedback bezogen auf Probleme mit der Barrierefreiheit in deinem JSX zur Verfügung. Viele IDE’s machen es dir möglich diese Funktionalität direkt in die Code-Analyse oder die Coding-Oberfläche zu integrieren.

Create React App verfügt über ein Plugin welches einen Teil dieser Regeln aktiviert hat. Solltest du noch mehr Regeln zur Barrierefreiheit nutzen wollen, kannst im Stammverzeichnis deines Projekts eine .eslintrc-Datei mit folgendem Inhalt anlegen:

{
  "extends": ["react-app", "plugin:jsx-a11y/recommended"],
  "plugins": ["jsx-a11y"]
}

Barrierefreiheit im Browser testen

Es gibt eine Reihe von Werkzeugen, die Barrierefreiheitsprüfungen auf deinen Webseiten im Browser durchführen können. Bitte nutze Diese nur in Verbindung mit den bereits genannten Werkzeugen, da diese Tests nur die technische Barrierefreiheit in deinem HTML kontrollieren können.

aXe, aXe-core und react-axe

Deque Systems bietet aXe-core für automatisierte End-to-End-Tests deiner Anwendung an. Dieses Modul enthält auch Einbindungen für Selenium.

The Accessibility Engine, oder aXe, ist eine Browser-Erweiterung basierend auf aXe-core, die einen Inspektor für Barrierefreiheit bereitstellt.

Außerdem kannst du das react-axe-Modul nutzen, um die Ergebnisse dieser Barrierefreiheitskontrollen während der Entwicklung direkt in der Konsole auszugeben.

WebAIM WAVE

Das Web Accessibility Evaluation Tool ist eine weitere Browser-Erweiterung.

Barrierefreiheitsprüfer und der Barrierefreiheitsbaum

Der Barrierefreiheitsbaum ist eine Teilmenge des DOM-Baums, welche barrierefreie Objekte für jedes DOM-Element, das für unterstützende Technologie wie z. B. Screen-Readern sichtbar sein sollte, enthält.

In einigen Browsern ist es einfach möglich die Informationen zur Barrierefreiheit für jedes Element des Barrierefreiheitsbaums zu überprüfen:

Screen-Reader

Das Testen mit einem Screen-Reader sollte bei deinen Tests zur Barrierefreiheit nicht fehlen.

Beachte dass es auf die Browser / Screen-Reader Kombination ankommt. Es wird empfohlen, dass du deine Anwendung in dem Browser testest, der am besten zu deinem gewählten Screen-Reader passt.

Häufig genutzte Screen-Reader

NVDA im Firefox

NonVisual Desktop Access, auch NVDA, ist ein häufig genutzter Screen-Reader für Windows.

Nutze die folgenden Hinweise, um zu erfahren wie NVDA am besten zu nutzen ist:

VoiceOver im Safari

VoiceOver ist ein in Apple-Geräte integrierter Screen-Reader.

Nutze die folgenden Hinweise, um zu erfahren wie man VoiceOver aktiviert:

JAWS im Internet Explorer

Job Access With Speech, auch JAWS, ist ein häufig genutzter Screen-Reader für Windows.

Nutze die folgenden Hinweise, um zu erfahren wie JAWS am besten zu nutzen ist:

Weitere Screen-Reader

ChromeVox im Google Chrome

ChromeVox ist ein in Chromebooks integrierter Screen-Reader und ist außerdem als eine Erweiterung für Chrome verfügbar.

Nutze die folgenden Hinweise, um zu erfahren wie ChromeVox am besten zu nutzen ist: