Aufbau einer React-Komponente
Meine Vorstellung davon, wie jede React-Komponente aussehen sollte
421
8 min.
Der SST-Standard legt Anforderungen an die Semantik, Barrierefreiheit und Funktionslogik von Registerkarten fest.
overflow-x: auto. Nur die Leiste scrollt horizontal, nicht die gesamte Seite; der Inhalt des geöffneten Panels muss sich an die Viewportbreite anpassen.10–20px. Dies signalisiert, dass die Liste weitergeht, und kann dekorative Pfeilindikatoren überflüssig machen. Die Beschriftung oder der Fokusindikator des aktiven Tabs darf nicht abgeschnitten werden; dieser visuelle Hinweis ersetzt keine Tastaturnavigation.-webkit-overflow-scrolling: touch ist keine grundlegende Voraussetzung für flüssiges Scrollen. Aktuelle iOS- und iPadOS-Versionen beschleunigen das Scrollen in Overflow-Bereichen auch ohne diese Eigenschaft; auf dem iPhone kann sie außerdem einen Stacking Context erzeugen. Sie wird nur für ausdrücklich unterstützte ältere Versionen und nach Tests auf Geräten ergänzt.Der aktive Tab erhält einen deutlichen visuellen Zustand, zum Beispiel eine Akzentlinie oder einen geänderten Hintergrund. Der Unterschied darf nicht allein durch Farbe vermittelt werden.
Bei der Auswahl eines Tabs, einer Bewegung des Tastaturfokus oder beim Öffnen eines direkten Links wird die Leiste so gescrollt, dass der ausgewählte Tab sichtbar und möglichst nahe an der Mitte liegt.
Die Zentrierung ist durch den verfügbaren Scrollbereich begrenzt: Der erste und letzte Tab am Rand des Containers können nicht immer exakt mittig positioniert werden.
Für eine Links-nach-rechts-Ausrichtung lässt sich der Zielwert relativ zum Anfang des scrollbaren Inhalts berechnen:
targetScrollLeft = clamp(tabStart + tabWidth / 2 - viewportWidth / 2, 0, scrollWidth - viewportWidth)
Dabei ist tabStart die Position des Tabs relativ zum Anfang der Scroll-Leiste. Bei RTL-Layouts sind Schreibrichtung und Unterschiede beim horizontalen Scrollverhalten zu berücksichtigen; nach Möglichkeit wird logisches Scrollen des Elements verwendet.
role="tablist" und einen zugänglichen Namen, entweder über eine sichtbare Überschrift mit aria-labelledby oder über aria-label.role="tab", eine eindeutige, stabile id und aria-controls="panel-id", das auf das zugehörige Panel verweist.role="tabpanel", eine eindeutige id und aria-labelledby="tab-id", das auf den steuernden Tab verweist.aria-selected="true"; alle anderen Tabs haben aria-selected="false". Inaktive Panels werden beispielsweise mit dem Attribut hidden ausgeblendet.tabindex="0", alle anderen tabindex="-1". Ein Panel ohne fokussierbare Inhalte kann tabindex="0" erhalten, damit es in der Tastaturreihenfolge erreichbar ist.#tab-id. Es werden URL-sichere Kennungen verwendet oder Werte beim Lesen des Hashes einheitlich kodiert und dekodiert.history.replaceState aktualisiert den aktuellen Eintrag, ohne für jede Auswahl einen eigenen Zurück-Schritt hinzuzufügen. Die Anwendung muss den Tab-Zustand im selben Handler aktualisieren und darf sich dafür nicht auf ein hashchange-Event verlassen.history.pushState verwendet und der Tab-Zustand bei popstate synchronisiert.tablist, tab und tabpanel werden nur für Tabs verwendet, die Panels auf derselben Seite umschalten. Für die Navigation zwischen Seiten werden Links verwendet, auch wenn sie wie Tabs gestaltet sind.Tab wird der Fokus auf den aktiven Tab in der Leiste gesetzt; das nächste Tab bewegt ihn aus der Tab-Liste zum Panel-Inhalt oder zum nächsten Seitenelement.← und → den Fokus zwischen Tabs und springen am Listenende zum jeweils anderen Ende. In RTL-Oberflächen muss die Tastenrichtung der visuellen Reihenfolge und Leserichtung entsprechen.Home und End kann der Fokus zum ersten beziehungsweise letzten Tab springen. Bei manueller Aktivierung wird der fokussierte Tab mit Enter oder Space aktiviert; bei automatischer Aktivierung folgt die Auswahl dem Fokus.aria-selected, die Beziehungen aria-controls und aria-labelledby sowie die Sichtbarkeit der Panels bleiben synchron.replaceState aktualisiert die Auswahl die Adresse, ohne für jeden Tab einen History-Eintrag anzulegen. Bei pushState synchronisieren Zurück und Vorwärts URL, aktiven Tab und Panel.prefers-reduced-motion: reduce nicht beeinträchtigen.Tab-Leiste mit Panels und ARIA-Beziehungen:
<div class="tabs__viewport">
<div class="tabs__list" role="tablist" aria-label="Dokumentabschnitte">
<button
id="tab-overview"
type="button"
role="tab"
aria-controls="panel-overview"
aria-selected="true"
tabindex="0"
>
Übersicht
</button>
<button
id="tab-details"
type="button"
role="tab"
aria-controls="panel-details"
aria-selected="false"
tabindex="-1"
>
Details
</button>
</div>
</div>
<section
id="panel-overview"
role="tabpanel"
aria-labelledby="tab-overview"
tabindex="0"
>
Inhalt des Bereichs „Übersicht“.
</section>
<section
id="panel-details"
role="tabpanel"
aria-labelledby="tab-details"
tabindex="0"
hidden
>
Inhalt des Bereichs „Details“.
</section>CSS für eine einzeilige scrollbare Leiste:
.tabs__viewport {
overflow-x: auto;
padding: 2px;
scroll-behavior: smooth;
}
.tabs__list {
display: flex;
flex-wrap: nowrap;
width: max-content;
min-width: 100%;
}
.tabs__list [role="tab"] {
flex: 0 0 auto;
white-space: nowrap;
}
.tabs__list [role="tab"][aria-selected="true"] {
border-block-end: 2px solid currentColor;
}
.tabs__list [role="tab"]:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
@media (max-width: 767px) {
.tabs__viewport {
scrollbar-width: none;
}
.tabs__viewport::-webkit-scrollbar {
display: none;
}
}
@media (prefers-reduced-motion: reduce) {
.tabs__viewport {
scroll-behavior: auto;
}
}Auf Mobilgeräten wird die ausgeblendete Scrollleiste durch einen teilweise sichtbaren nächsten Tab kompensiert. Der sichtbare Anteil hängt vom Container und den Beschriftungen ab; aktiver Tab und Fokusindikator bleiben vollständig sichtbar.
Der Breakpoint 767px im Media Query ist ein Beispiel und keine SST-A-Vorgabe. Er wird durch den in der responsiven Projektsystematik verwendeten Breakpoint ersetzt.
Tab zentrieren und URL synchronisieren:
const centerTab = (tab) => {
tab.scrollIntoView({
block: 'nearest',
inline: 'center'
})
}
const findTabFromUrl = (tabs) => {
const tabId = window.location.hash.slice(1)
return tabs.find((tab) => tab.id === tabId) || null
}
const updateTabUrl = (tab) => {
window.history.replaceState(null, '', '#' + tab.id)
}Bei der Initialisierung wird der über findTabFromUrl gefundene Tab zusammen mit seinem Panel aktiviert und an centerTab übergeben. Bei einer Benutzerauswahl werden zuerst aria-selected, roving tabindex und die Sichtbarkeit der Panels aktualisiert; danach werden centerTab und updateTabUrl aufgerufen. Dieses Beispiel zeigt die Synchronisierung von Zustand, URL und Scrollposition; Tastatur-Handler werden gemäß den obigen Regeln implementiert.
Meine Vorstellung davon, wie jede React-Komponente aussehen sollte
421
8 min.
Der SSF-U-Standard legt Anforderungen an die Semantik, Barrierefreiheit und Funktionslogik von Vollbildmodus fest
248
2 min.
Der SSA-Standard legt Anforderungen an die Semantik, Barrierefreiheit und Funktionslogik von Akkordeons fest
258
2 min.
Analyse kritischer Fehler im Webdesign. Warum Slider, Autoplay und schwerfällige Seiten die Konversionsrate sowie die Platzierungen bei Google verschlechtern
232
2 min.
Der SSP-Standard legt Anforderungen an die Semantik, Barrierefreiheit und Funktionslogik von Paginierung fest
240
1 min.
Der SSPS-Standard legt Anforderungen an die Struktur und Benennung von Dateien und Ordnern im Projekt fest
345
2 min.