Hallo Sascha
Ich habe eine Seite mit Tabs, die per CSS gesteuert werden.
Nein, hast du nicht.
Jedenfalls nicht, wenn du damit die Seite meinst, deren Code du hier gepostet hast. Zwei Radiobuttons und ein paar div
-Elemente ohne besondere Bedeutung sind noch keine Tabs. Es sind nur zwei Radiobuttons und ein paar div
-Elemente ohne besondere Bedeutung. Wenn du Tabs willst, dann musst du das im Markup entsprechend kenntlich machen. Außerdem musst du mit JavaScript dafür sorgen, dass die Tabs den Erwartungen der Benutzer gemäß bedient werden können. Eine Implementierung von Tabs nur mit CSS kannst du vergessen.
Die Umsetzung eines Tabbed Interface ist keine ganz triviale Aufgabe. Damit ein solches Widget von allen Benutzern erkannt und bedient werden kann, müssen viele Aspekte beachtet werden. Es genügt nicht, etwas bloß wie Tabs aussehen zu lassen. Der Zweck der Elemente muss auch für diejenigen Benutzer erkennbar sein, die sich die Inhalte der Seite vorlesen lassen, statt sie selbst in Augenschein zu nehmen. Zusätzlich gibt es Konventionen in Bezug auf die Tastaturbedienbarkeit von Tabs, die zu berücksichtigen sind, um den Benutzern die Bedienung nicht unnötig zu erschweren.
Was ist bei der Implementierung von Tabs zu beachten?
Fallback
Bevor man sich überlegt, wie Tabs implementiert werden könnten, sollte man sich erst einmal Gedanken darüber machen, wie die Inhalte strukturiert sein sollen, wenn JavaScript nicht zur Verfügung steht. Eine korrekte Implementierung von Tabs ist ohne die Verwendung von JavaScript nicht möglich, aber es gibt tausend Gründe, aus denen ein Programm nicht geladen oder ausgeführt werden kann. Wenn du nicht mehr oder weniger willkürlich Benutzer von der Bedienung deiner Seite ausschließen willst, brauchst du also eine Alternative, welche nicht von der Ausführung eines Skripts abhängig ist.
Die übliche Vorgehensweise in diesem Fall ist, die Inhalte der Tabs grundsätzlich anzuzeigen und sie über eine seiteninterne Navigation zu verlinken. Steht JavaScript zur Verfügung, werden programmatisch alle Inhalte ausgeblendet, außer denjenigen, die zum anfänglich ausgewählten Tab gehören. Die seiteninterne Navigation wird durch das Skript entweder in eine Liste von Tabs umgebaut, oder durch eine solche Liste ersetzt.
Die Navigation zu ersetzen ist meiner Meinung nach dem Umbau vorzuziehen. Es ist mit weniger Aufwand verbunden und verbessert die Les- und Wartbarkeit des Codes. Das Markup für die Liste mit den Tabs kann in einem template
-Element notiert werden. Der Inhalt dieses Elements wird zusammen mit dem Rest der Seite geparst, aber nicht gerendert. Ein Programm kann über die content
-Eigenschaft dieses Elements auf dessen Inhalt zugreifen und ihn an der gewünschten Stelle in die Seite einfügen. Die dort bereits vorhandene Navigation wird im gleichen Schritt entfernt.
Erkennbarkeit
Mit den Komponenten eines Tabbed Interface sind bestimmte Rollen verknüpft. Damit alle Benutzer die Chance haben, die Bedeutung eines solchen Widgets zu erkennen, müssen diese Rollen den Elementen, welche die jeweiligen Komponenten repräsentieren, unter Verwendung des role
-Attributs ausdrücklich zugewiesen werden. Durch das Hinzufügen des Attributs wird die ursprüngliche Semantik der Elemente überschrieben. Zur Verbesserung der Bedienbarkeit ist darüber hinaus dafür zu sorgen, dass alle verwendeten Elemente einen passenden Accessible Name besitzen, also einen Namen, der eine nützliche Beschreibung des jeweiligen Elements ermöglicht. Schließlich ist es auch erforderlich, dass der aktuelle Zustand des Widgets in einer allgemein verständlichen Weise kenntlich gemacht wird.
Tabliste
Dem Element, das die Tabs gruppiert, wird die Rolle tablist
zugewiesen. Dabei kann es sich im Prinzip um irgendein Element handeln. Das heißt, es wäre nicht falsch, an dieser Stelle ein div
zu verwenden. Gemäß dem Fall, dass eine bereits existierende Navigation umgebaut werden soll, wäre die Liste mit den Links das Ziel dieser Rollenzuweisung. Wird eine geordnete oder ungeordnete Liste wiederverwendet, sollte außerdem den Kindern der Liste, also den li
-Elementen, die Rolle none
zugewiesen werden. Die Kindelemente einer Tabliste sind Tabs und sonst nichts. Die List Items sind überflüssig.
Das Hinzufügen der Rolle none
veranlasst den Browser, die Existenz der li
-Elemente gegenüber anderen Anwendungen zu verbergen. Elemente mit dieser Rolle werden nicht in die Repräsentation der Seite aufgenommen, die über die Accessibility API des jeweiligen Betriebssystems anderen Programmen zugänglich gemacht wird. Aus Sicht von assistiver Software wie zum Beispiel Screen Readern, die über diese Schnittstellen auf den Browser und die darin angezeigte Webseite zugreifen, sind die Tabs dann Kinder der Tabliste.
Zu beachten ist in diesem Zusammenhang aber, dass gegenwärtig, als Fallback oder Ersatz für die Rolle none
, die Rolle presentation
angegeben werden sollte. Die Bedeutung der beiden Rollen ist zwar identisch und die Verwendung der Rolle none
wird grundsätzlich empfohlen, aber die Rolle presentation
genießt noch die breitere Unterstützung.
Neben der entsprechenden Rolle, sollte für die Tabliste auch ein aussagekräftiger Name angegeben werden. Sehende Benutzer können sich hier schnell einen Überblick verschaffen, aber Benutzer, die sich die Seite vorlesen lassen, müssten ohne zusätzliche Informationen erst durch die ganze Liste navigieren, um zu verstehen, welche Inhalte in dem Widget zusammengefasst sind. Mit dem Attribut aria-label
kann ein passender Name hinzugefügt werden. Der Name muss nicht zwingend ein einzelner Bezeichner sein. Es kann auch eine kurze, aus wenigen Worten bestehende Beschreibung sein. Dabei sollte allerdings vermieden werden, von einer „Liste“ oder „Tabliste“ zu sprechen, da diese Information bereits aus der Rolle des Elements hervorgeht. Von Screen Readern werden die Rolle und der Name üblicherweise nacheinander vorgelesen.
Tabs
Den Elementen, die Tabs repräsentieren, wird die Rolle tab
zugewiesen. Als Basis für diese Komponenten würde ich Buttons verwenden. Hast du dich hingegen entschieden, eine bestehende Navigation umzubauen, erhalten die Links die Rolle tab
. Außerdem sollten die Tabs über eine id
verfügen, damit über die Elemente, die mit den Tabs verknüpft sind, auf den jeweils dazugehörigen Tab Bezug genommen werden kann. Ein Name für die Tabs muss nicht über ein Attribut angegeben werden. Der Name ist in diesem Fall einfach der textuelle Inhalt der Elemente.
Was auf den Tabs allerdings vermerkt werden sollte, ist die Information, ob der einzelne Tab gerade ausgewählt ist oder nicht. Das heißt, über die Tabs wird der aktuelle Zustand des Widgets kommuniziert. Zu diesem Zweck wird das Attribut aria-selected
hinzugefügt, mit dem Wert true
für den gerade ausgewählten Tab. Hierbei ist zu beachten, dass es sich bei diesem Attribut nicht um ein boolesches Attribut im engeren Sinne handelt, sprich, es gibt neben den Zuständen true
und false
noch den Zustand undefined
, der soviel bedeutet wie: „Dieses Element kann nicht ausgewählt werden.“ Dabei handelt es sich außerdem um den Standardzustand. Für die Tabs, die gerade nicht ausgewählt sind, ist das Attribut also anzugeben und explizit auf den Wert false
zu setzen.
Es könnte zudem auf den Tabs das Attribut aria-controls
definiert werden. Das halte ich beim derzeitigen Stand der Dinge aber nicht für sinnvoll, weshalb ich von dessen Gebrauch abraten würde. In diesem Beitrag findest du etwas mehr Informationen dazu.
Tabpanels
Den Elementen, die den Inhalt eines Tabs repräsentieren, wird die Rolle tabpanel
zugewiesen. Als Basiselement wird hier regelmäßig section
eine gute Wahl sein. Der Name eines Tabpanels sollte dem Namen des dazugehörigen Tabs entsprechen. Dieser muss jedoch nicht ausdrücklich angegeben werden. Mit dem Attribut aria-labelledby
kann auf den Namen des zu dem Panel gehörenden Tabs verwiesen werden, indem als Attributwert eine Referenz auf die id
des Tabs notiert wird. Das ist insgesamt die robustere Lösung, da es hiermit nur noch eine "Quelle der Wahrheit" gibt. Wird der textuelle Inhalt eines Tabs verändert, dann wird der Name des assoziierten Tabpanels automatisch angepasst. Inkonsistente Beschriftungen sind damit ausgeschlossen.
Dass der Inhalt eines nicht ausgewählten Tabs verborgen ist, sollte ebenfalls im Markup notiert werden. Hierfür wird das Attribut hidden
verwendet. Zu beachten ist hier, dass beim hidden
-Attribut die Präsenz des Attributs den Zustand true
repräsentiert und die Abwesenheit den false
-Zustand. Wird das Attribut gesetzt, kann der Wert weggelassen werden. Oder es wird der leere String oder der Name des Attributs angegeben.
Bedienbarkeit
Durch eine Tabliste wird bemerkenswerterweise nicht mit der Tabulatortaste navigiert, sondern mit den Pfeiltasten. Ist die Tabliste horizontal orientiert, wird mit den Tasten "Pfeil nach rechts" und "Pfeil nach links" von einem Tab zum nächsten gesprungen. Ist die Tabliste hingegen vertikal orientiert, werden die Tasten "Pfeil nach unten" und "Pfeil nach oben" verwendet. Dabei wird standardmäßig davon ausgegangen, dass die Liste der Tabs horizontal orientiert ist. Ist sie das nicht oder nicht mehr, sollte man das angeben, was mit Hilfe des aria-orientation
-Attributs bewerkstelligt werden kann. Das Attribut wird der Tabliste hinzugefügt und bei einer vertikalen Ausrichtung auf vertical
gesetzt.
Hier ist zu beachten, dass eine vertikale Ausrichtung der Tabliste nicht bereits dann vorliegt, wenn die Tabliste automatisch umgebrochen wurde und infolgedessen ein paar der Tabs untereinander dargestellt werden. Der Sinn einer Angabe zur Ausrichtung der Tabliste und der Anpassung der Navigationstasten an die Ausrichtung besteht darin, Bedienung und optisches Erscheinungsbild konsistent zu halten. Nicht alle Benutzer, die ein solches Widget mit der Tastatur bedienen, sind in ihrer Sehfähigkeit eingeschränkt. Es wäre aus Benutzersicht ein unerwartetes Verhalten, wenn die Tabs auf dem Bildschirm untereinander angeordnet sind, die Bedienung aber mit den horizontalen Pfeiltasten erfolgen soll.
Anzumerken wäre an dieser Stelle vielleicht noch, dass die Verwendung von Tabs bei schmalen Viewports nicht die beste Idee ist. Die Methode matchMedia
kann dazu verwendet werden, programmatisch zu überprüfen, ob die Zusammenfassung von Inhalten in Tabs auf der Seite überhaupt sinnvoll ist. Ist der Viewport zu schmal, kann man sich die Ausführung sparen und es bei der als Fallback vorgesehenen Darstellung belassen.
Mit diesem Beitrag geht’s weiter.