Technik

Barrierefreie Tabellen: Preislisten, Kurspläne und Vergleiche richtig auszeichnen

Von Joshua Kantner · April 2026 · bf-check.de

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:

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 &euro;</td>
    </tr>
    <tr>
      <th scope="row">Business</th>
      <td>50 GB</td>
      <td>14,99 &euro;</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.

⚠️
Ist deine Webseite betroffen? Kostenloser BFSG-Schnellcheck – Ergebnis in 30 Sekunden.
Jetzt prüfen →

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 &euro;</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)

  1. Öffne die Seite in Firefox oder Chrome.
  2. Drücke Einfg + F5, um die Elementliste zu öffnen, und filtere nach „Tabellen“.
  3. Navigiere mit Strg + Alt + Pfeiltasten durch die Tabelle.
  4. Prüfe: Liest NVDA bei jeder Datenzelle die zugehörige Spalten- und Zeilenüberschrift vor?
  5. Prüfe: Wird die caption beim Betreten der Tabelle angesagt?

Test mit JAWS (kommerziell, Windows)

  1. Navigiere mit T zur nächsten Tabelle.
  2. Nutze Strg + Alt + Pfeiltasten für die Zellennavigation.
  3. Drücke Einfg + F5 für Tabelleninformationen (Zeilen-/Spaltenanzahl).

Test mit VoiceOver (macOS/iOS)

  1. Aktiviere VoiceOver mit Cmd + F5.
  2. Navigiere mit VO + Pfeiltasten durch die Tabelle.
  3. 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:

  1. 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 mit role="presentation" kennzeichnen.
  2. caption ergänzen: Jede Datentabelle braucht ein <caption>-Element direkt nach dem öffnenden <table>-Tag. Beschreibe kurz, was die Tabelle enthält.
  3. Header-Zellen auszeichnen: Ersetze <td> in Überschriftenzeilen/-spalten durch <th>. Füge scope="col" oder scope="row" hinzu.
  4. Komplexe Zuordnungen prüfen: Gibt es colspan oder rowspan? Dann wechsle zu headers/id statt scope.
  5. 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:

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

Darf ich div-Elemente statt einer HTML-Tabelle verwenden?
Für tabellarische Daten: Nein. Verwende immer echte HTML-Tabellen mit 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.
Was verlangt WCAG SC 1.3.1 konkret bei Tabellen?
SC 1.3.1 (Info and Relationships) fordert, dass Informationen und Beziehungen, die visuell erkennbar sind, auch programmatisch bestimmbar sind. Bei Tabellen bedeutet das: Spalten- und Zeilenüberschriften mit th und scope, eine beschreibende caption und bei komplexen Tabellen die Zuordnung über headers und id.
Wann brauche ich headers und id statt scope?
Bei komplexen Tabellen mit mehreren Überschriftenebenen, zusammengeführten Zellen (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.
Wie teste ich meine Tabelle mit einem Screenreader?
Installiere NVDA (kostenlos) oder nutze VoiceOver (macOS/iOS). Navigiere mit Strg+Alt+Pfeiltasten (NVDA) durch die Tabelle. Prüfe, ob der Screenreader bei jeder Zelle die zugehörige Spalten- und Zeilenüberschrift vorliest. Fehlt die Ansage, fehlen scope- oder headers-Attribute.
Ist role="presentation" bei Layout-Tabellen Pflicht?
Wenn du noch Legacy-Code mit Layout-Tabellen hast, entfernt 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.
Wie mache ich Tabellen responsive, ohne die Barrierefreiheit zu verlieren?
Wickle die Tabelle in einen Container mit 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.
Brauchen Tabellen einen Titel oder reicht eine Überschrift darüber?
Das 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

Technik
Barrierefreie Formulare
Technik
Videos barrierefrei
Leitfaden
BFSG 2025: Der komplette Leitfaden

Ist deine Webseite BFSG-konform?

Finde es in 30 Sekunden heraus – kostenloser Schnellcheck mit sofortigem Ergebnis.

Jetzt kostenlos prüfen →
BFSG-Pflicht seit Juni 2025 – Ist deine Seite konform? Kostenlos prüfen
Seit Juni 2025 Pflicht

Warte – deine Webseite könnte gegen das BFSG verstoßen

Abmahnungen bis 5.000 €, Bußgelder bis 100.000 €. Unser kostenloser Scan zeigt dir in 30 Sekunden ob du betroffen bist.

Jetzt kostenlos scannen →