Barrierefreie Tabellen: Preislisten, Kurspläne und Vergleiche richtig auszeichnen
Wann eine Tabelle die richtige Wahl ist
Tabellen sind für tabellarische Daten gedacht: Preislisten, Stundenpläne, Vergleiche, Spezifikationen. Sie sind nicht für Layouts gedacht. Wenn du eine Tabelle für das Seitenlayout nutzt, ist das ein Barrierefreiheits-Problem, weil Screenreader sie als Datentabelle interpretieren und Nutzer:innen durch vermeintliche Zeilen und Spalten navigieren lassen.
Die Faustregel: Stehen die Daten in einer logischen Beziehung zueinander, bei der Zeilen und Spalten eine Bedeutung tragen? Dann ist <table> richtig. Geht es nur darum, Elemente nebeneinander anzuordnen? Dann nutze CSS Grid oder Flexbox.
Typische Anwendungsfälle für echte Datentabellen:
- Preislisten mit Produkt, Menge und Preis
- Kurspläne mit Wochentag, Uhrzeit und Kursname
- Produktvergleiche mit Features in Zeilen und Produkten in Spalten
- Fahrpläne, Statistiken, technische Spezifikationen
Was WCAG SC 1.3.1 bei Tabellen verlangt
Das Erfolgskriterium SC 1.3.1 – Info and Relationships (Stufe A) ist die zentrale WCAG-Anforderung für Tabellen. Es fordert: Informationen, Strukturen und Beziehungen, die visuell vermittelt werden, müssen auch programmatisch bestimmbar sein oder im Text verfügbar gemacht werden.
Bei einer Preisliste erkennt ein sehender Nutzer sofort, dass „29,90 €“ zum Produkt „Basis-Paket“ gehört, weil beide in derselben Zeile stehen und die Spaltenüberschrift „Preis“ darüber steht. Ein Screenreader kann diese Beziehung nur erkennen, wenn die Tabelle korrekt ausgezeichnet ist – mit <th>, scope und gegebenenfalls headers/id.
Zusätzlich relevant ist SC 1.3.2 – Meaningful Sequence (Stufe A): Die Lesereihenfolge im DOM muss sinnvoll sein. Tabellen, die per CSS visuell umgestellt werden, müssen im Quellcode trotzdem logisch aufgebaut bleiben.
Korrektes Tabellen-Markup: Schritt für Schritt
Einfache Tabelle mit caption und scope
Das Grundgerüst einer barrierefreien Tabelle besteht aus vier Bausteinen: <caption> für die Beschreibung, <thead> für den Kopfbereich, <th scope="col"> für Spaltenüberschriften und <th scope="row"> für Zeilenüberschriften.
<table>
<caption>Preisliste Hosting-Pakete (Stand April 2026)</caption>
<thead>
<tr>
<th scope="col">Paket</th>
<th scope="col">Speicher</th>
<th scope="col">Preis / Monat</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Starter</th>
<td>10 GB</td>
<td>4,99 €</td>
</tr>
<tr>
<th scope="row">Business</th>
<td>50 GB</td>
<td>14,99 €</td>
</tr>
</tbody>
</table>
Warum das funktioniert: NVDA liest beim Navigieren in die Zelle „50 GB“ automatisch vor: „Spalte Speicher, Zeile Business, 50 GB“. Ohne scope würde nur „50 GB“ vorgelesen – der Kontext geht verloren.
Komplexe Tabellen mit headers und id
Sobald Zellen zusammengeführt werden (colspan, rowspan) oder mehrere Überschriftenebenen existieren, reicht scope nicht mehr aus. Dann ordnest du jeder Überschriftenzelle eine eindeutige id zu und referenzierst diese in den Datenzellen über das headers-Attribut.
<table>
<caption>Kursplan Fitnessstudio (Mo–Mi)</caption>
<thead>
<tr>
<td></td>
<th id="mo" scope="col">Montag</th>
<th id="di" scope="col">Dienstag</th>
<th id="mi" scope="col">Mittwoch</th>
</tr>
</thead>
<tbody>
<tr>
<th id="morgen" scope="row">09:00</th>
<td headers="mo morgen">Yoga</td>
<td headers="di morgen">Spinning</td>
<td headers="mi morgen">Pilates</td>
</tr>
<tr>
<th id="mittag" scope="row">12:00</th>
<td headers="mo mittag" colspan="2">Krafttraining (Mo+Di)</td>
<td headers="mi mittag">HIIT</td>
</tr>
</tbody>
</table>
Bei der zusammengeführten Zelle „Krafttraining (Mo+Di)“ liest ein Screenreader dank headers="mo mittag" den korrekten Kontext vor. Ohne diese Zuordnung wäre unklar, zu welchem Tag die Zelle gehört.
Spalten- und Zeilengruppen mit colgroup und rowgroup
Für besonders umfangreiche Tabellen – etwa Produktvergleiche mit Kategorien – bieten <colgroup> und scope="rowgroup" zusätzliche Strukturierung:
<table>
<caption>Produktvergleich Barrierefreiheits-Tools</caption>
<colgroup><col></colgroup>
<colgroup span="3"></colgroup>
<thead>
<tr>
<td></td>
<th scope="col">Tool A</th>
<th scope="col">Tool B</th>
<th scope="col">Tool C</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="rowgroup" colspan="4">Automatische Prüfungen</th>
</tr>
<tr>
<th scope="row">Farbkontrast</th>
<td>Ja</td><td>Ja</td><td>Nein</td>
</tr>
<tr>
<th scope="row">Alt-Texte</th>
<td>Ja</td><td>Nein</td><td>Ja</td>
</tr>
</tbody>
</table>
Der scope="rowgroup" auf der Kategorie-Zeile signalisiert dem Screenreader, dass die folgenden Zeilen zu dieser Gruppe gehören. So wird die Hierarchie auch ohne visuelle Einrückung klar.
Responsive Tabellen ohne Barrierefreiheits-Verlust
Tabellen auf Mobilgeräten sind problematisch: Breite Tabellen sprengen den Viewport, horizontales Scrollen ist umständlich. Es gibt zwei bewährte Patterns, die die Semantik erhalten.
Pattern 1: Scrollbarer Container
Wickle die Tabelle in ein <div> mit overflow-x: auto. Damit Tastaturnutzer:innen den Container scrollen können, füge tabindex="0", role="region" und ein aria-label hinzu:
<div role="region" aria-label="Preisliste" tabindex="0"
style="overflow-x: auto;">
<table>
<!-- Tabelle bleibt unverändert -->
</table>
</div>
Dieses Pattern eignet sich besonders für Tabellen mit vielen Spalten, bei denen ein Umbruch die Lesbarkeit zerstören würde.
Pattern 2: CSS-Reflow (Karten-Darstellung)
Per Media Query wandelst du jede Zeile in eine „Karte“ um. Die Spaltenüberschrift wird per data-label-Attribut oder CSS content vor jede Zelle gesetzt:
<!-- HTML: data-label auf jede td -->
<td data-label="Preis">14,99 €</td>
/* CSS */
@media (max-width: 600px) {
table, thead, tbody, tr, th, td {
display: block;
}
thead {
/* Visuell verstecken, aber für Screenreader erhalten */
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip: rect(0,0,0,0);
}
td::before {
content: attr(data-label) ": ";
font-weight: 600;
}
tr {
margin-bottom: 1rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 0.75rem;
}
}
Wichtig: Die semantische Struktur (th, td, scope) muss im HTML erhalten bleiben, auch wenn die Darstellung sich ändert. Der Screenreader liest das DOM, nicht das gerenderte Layout. Verstecke <thead> mit der Clip-Methode, nicht mit display: none – sonst geht die Semantik für assistive Technologien verloren.
Tabellen mit Screenreadern testen: NVDA und JAWS
Automatische Tests mit dem bf-check Scanner decken fehlende th-Elemente und scope-Attribute auf. Für die volle Sicherheit brauchst du aber einen manuellen Screenreader-Test.
Test mit NVDA (kostenlos, Windows)
- Öffne die Seite in Firefox oder Chrome.
- Drücke Einfg + F5, um die Elementliste zu öffnen, und filtere nach „Tabellen“.
- Navigiere mit Strg + Alt + Pfeiltasten durch die Tabelle.
- Prüfe: Liest NVDA bei jeder Datenzelle die zugehörige Spalten- und Zeilenüberschrift vor?
- Prüfe: Wird die
captionbeim Betreten der Tabelle angesagt?
Test mit JAWS (kommerziell, Windows)
- Navigiere mit T zur nächsten Tabelle.
- Nutze Strg + Alt + Pfeiltasten für die Zellennavigation.
- Drücke Einfg + F5 für Tabelleninformationen (Zeilen-/Spaltenanzahl).
Test mit VoiceOver (macOS/iOS)
- Aktiviere VoiceOver mit Cmd + F5.
- Navigiere mit VO + Pfeiltasten durch die Tabelle.
- VoiceOver sagt beim Betreten die Tabellendimensionen und die Caption an.
Wenn bei einem dieser Tests die Spaltenüberschrift fehlt, ist das ein direkter Verstoß gegen SC 1.3.1. Korrigiere dann scope-Attribute oder ergänze headers/id.
Schritt-für-Schritt: Bestehende Tabellen barrierefrei machen
Du hast bereits Tabellen auf deiner Webseite? Mit diesen fünf Schritten machst du sie WCAG-konform:
- Layout-Tabellen identifizieren: Durchsuche deinen Quellcode nach
<table>-Elementen. Wird die Tabelle für Daten oder für Layout verwendet? Layout-Tabellen durch CSS Grid/Flexbox ersetzen oder mitrole="presentation"kennzeichnen. - caption ergänzen: Jede Datentabelle braucht ein
<caption>-Element direkt nach dem öffnenden<table>-Tag. Beschreibe kurz, was die Tabelle enthält. - Header-Zellen auszeichnen: Ersetze
<td>in Überschriftenzeilen/-spalten durch<th>. Fügescope="col"oderscope="row"hinzu. - Komplexe Zuordnungen prüfen: Gibt es
colspanoderrowspan? Dann wechsle zuheaders/idstattscope. - Responsivität sicherstellen: Wähle entweder den scrollbaren Container oder das CSS-Reflow-Pattern. Teste auf einem echten Mobilgerät.
Nach der Umsetzung: Lass deinen kostenlosen BFSG-Schnellcheck laufen, um automatisch prüfbare Fehler zu finden. Ergänze dann den manuellen Screenreader-Test.
Häufige Fehler bei Tabellen – und wie du sie vermeidest
Fehler 1: Layout-Tabellen ohne role="presentation"
Ältere Webseiten nutzen oft <table> für das Seitenlayout. Screenreader interpretieren diese als Datentabelle und kündigen „Tabelle mit X Zeilen und Y Spalten“ an. Die Lösung: role="presentation" auf das <table>-Element setzen – oder besser: auf CSS Grid/Flexbox umstellen.
Fehler 2: Fehlende Spaltenüberschriften
Viele Entwickler:innen nutzen <td> für die erste Zeile und stylen sie fett – visuell sieht das wie eine Überschrift aus, aber der Screenreader erkennt keine Beziehung. Immer <th scope="col"> verwenden.
Fehler 3: role="presentation" auf Datentabellen
Das Gegenteil des ersten Fehlers: role="presentation" auf einer echten Datentabelle entfernt die gesamte Tabellensemantik. Screenreader lesen dann nur noch eine Aneinanderreihung von Texten. Dieser Fehler verstößt direkt gegen SC 1.3.1.
Fehler 4: Tabelle als Bild
Kurspläne oder Preislisten als Screenshot/Grafik einbinden macht den Inhalt für Screenreader, Vergrößerungssoftware und Suchmaschinen unsichtbar. Verwende immer echtes HTML-Markup. Wenn ein Bild zusätzlich angezeigt wird, füge die Daten auch als HTML-Tabelle ein.
Fehler 5: Leere th-Zellen ohne Kennzeichnung
Die obere linke Ecke in einem Stundenplan ist oft leer. Nutze hier ein leeres <td> (nicht <th>) oder ein <th> mit <span class="sr-only">Uhrzeit / Tag</span>, um Screenreadern Kontext zu geben.
Rechtlicher Rahmen: BFSG und ARIA-Attribute
Das Barrierefreiheitsstärkungsgesetz (BFSG, § 3 Abs. 1 Nr. 2) verpflichtet Anbieter digitaler Dienstleistungen, ihre Webseiten barrierefrei zu gestalten. Die technische Grundlage bildet die EN 301 549, die wiederum auf die WCAG 2.1 verweist. Für Tabellen sind insbesondere relevant:
- SC 1.3.1 Info and Relationships (Stufe A) – Tabellenstruktur muss programmatisch bestimmbar sein
- SC 1.3.2 Meaningful Sequence (Stufe A) – Lesereihenfolge im DOM muss sinnvoll bleiben
- SC 4.1.2 Name, Role, Value (Stufe A) – Interaktive Elemente in Tabellen (Buttons, Links) müssen zugängliche Namen haben
Verstöße gegen diese Kriterien können seit Juni 2025 zu Abmahnungen und Bußgeldern führen (§ 32 BFSG, Ordnungswidrigkeiten bis 100.000 €).
Dieser Artikel ersetzt keine Rechtsberatung. Bei konkreten rechtlichen Fragen wende dich an eine spezialisierte Kanzlei.
Häufig gestellte Fragen
th, td, caption und scope-Attributen. Screenreader sind auf diese Semantik angewiesen (WCAG SC 1.3.1). Für Layouts nutze div mit CSS Grid oder Flexbox.th und scope, eine beschreibende caption und bei komplexen Tabellen die Zuordnung über headers und id.colspan/rowspan) oder verschachtelten Gruppierungen reicht scope nicht aus. Dann ordnest du jeder th eine eindeutige id zu und referenzierst diese in den td-Elementen über das headers-Attribut.scope- oder headers-Attribute.role="presentation" die Tabellensemantik für Screenreader. Besser ist es, Layout-Tabellen komplett durch CSS Grid oder Flexbox zu ersetzen. role="presentation" darf niemals auf Datentabellen gesetzt werden.overflow-x: auto und tabindex="0" sowie role="region" und aria-label. Alternativ kannst du per CSS-Reflow jede Zeile in eine Karte umwandeln (display: block auf tr/td), wobei du per data-label-Attribut die Spaltenüberschrift vor jede Zelle setzt.caption-Element ist die programmatisch zugeordnete Beschreibung einer Tabelle und wird von Screenreadern automatisch vorgelesen. Eine Überschrift (h2/h3) darüber ist visuell hilfreich, ersetzt aber nicht caption, weil sie nicht direkt mit dem table-Element verknüpft ist.Weiterlesen
Ist deine Webseite BFSG-konform?
Finde es in 30 Sekunden heraus – kostenloser Schnellcheck mit sofortigem Ergebnis.
Jetzt kostenlos prüfen →