Häufige Verstöße

Fehlende Tabellenüberschriften: Warum Datentabellen th brauchen, nicht nur fetten Text

Schauen Sie sich mit Ihren Augen eine beliebige Datentabelle an, und die Kopfzeile ist offensichtlich – sie ist meist fett, vielleicht anders schattiert, sitzt oben oder am linken Rand. Ein Screenreader-Nutzer, der Zelle für Zelle navigiert, bekommt diesen visuellen Kontext nicht kostenlos; ohne echtes Markup, das jede Datenzelle mit ihrer Überschrift verknüpft, bedeutet das Durchgehen einer 40-zeiligen Tabelle, eine Wand aus bloßen Zahlen und Wörtern zu hören, ohne zu wissen, was irgendetwas davon bedeutet.

Dies fällt unter WCAG-Erfolgskriterium 1.3.1 (Info und Beziehungen), Stufe A.

Die Lösung für einfache Tabellen

Verwenden Sie <th> für Kopfzellen statt eines gestylten <td>, und setzen Sie scope, um festzulegen, ob die Überschrift für ihre Spalte oder ihre Zeile gilt:

<!-- Vorher: Überschriften sind nur fetter Text, keine echten Kopfzellen -->
<table>
  <tr><td><b>Name</b></td><td><b>Umsatz</b></td></tr>
  <tr><td>Acme GmbH</td><td>1,2 Mio. €</td></tr>
</table>

<!-- Nachher: echte Kopfzellen, ausdrücklich zugeordnet -->
<table>
  <thead>
    <tr><th scope="col">Name</th><th scope="col">Umsatz</th></tr>
  </thead>
  <tbody>
    <tr><td>Acme GmbH</td><td>1,2 Mio. €</td></tr>
  </tbody>
</table>

Ein Screenreader kann nun "Umsatz: 1,2 Mio. €" ankündigen, wenn er auf dieser Zelle landet, statt nur "1,2 Mio. €" – die scope="col"-Zuordnung ist es, die den Spaltennamen automatisch liefert.

Komplexe Tabellen benötigen explizite id/headers-Zuordnung

Einfaches scope versagt bei Tabellen mit mehrstufigen oder unregelmäßigen Überschriften (eine Tabelle mit sowohl Zeilen- als auch Spaltenüberschriften, oder zusammengeführten Kopfzellen, die mehrere Spalten überspannen). Dafür erhält jedes <th> eine eindeutige id, und jede Datenzelle listet über das headers-Attribut genau auf, welche Kopf-IDs gelten:

<table>
  <tr>
    <th id="q1">Q1</th>
    <th id="q2">Q2</th>
  </tr>
  <tr>
    <th id="umsatz">Umsatz</th>
    <td headers="umsatz q1">1,2 Mio. €</td>
    <td headers="umsatz q2">1,4 Mio. €</td>
  </tr>
</table>

Dies ist ausführlicher, aber der einzige zuverlässige Weg, die Bedeutung einer Datenzelle in einer tatsächlich mehrdimensionalen Tabelle eindeutig zu machen.

Das Erste, was zu prüfen ist: Ist dies überhaupt eine Datentabelle?

Tabellen, die rein für visuelles Layout verwendet werden (ein verbreitetes Muster bei älteren Websites und in manchem automatisch generiertem oder aus E-Mails exportiertem HTML), sollten überhaupt keine <th>-Elemente hinzugefügt bekommen – das würde Layout-Zellen fälschlicherweise als Datenüberschriften ankündigen. Wenn eine Tabelle keine logische Zeilen-und-Spalten-Beziehung zu vermitteln hat, ist die bessere Lösung meist, die Tabelle vollständig durch semantisches Nicht-Tabellen-Markup zu ersetzen (Divs und CSS Grid/Flexbox), statt Kopfzeilen-Markup auf etwas nachzurüsten, das nie wirklich tabellarische Daten waren.

Offizielle Referenzen

Häufige Fragen

Warum genügt fetter Text nicht als Tabellenkopf?
Fetter oder hinterlegter Text ist nur ein visueller Hinweis. Ein Screenreader, der Zelle für Zelle navigiert, braucht echtes `<th>`-Markup, um die zugehörige Überschrift anzusagen; reine Gestaltung vermittelt ihm nichts.
Wie zeichne ich Tabellenköpfe korrekt aus?
Verwenden Sie `<th>` für Kopfzellen und ergänzen Sie `scope=col` oder `scope=row`, damit jede Datenzelle der richtigen Überschrift zugeordnet ist. Bei komplexen Tabellen verknüpfen Sie Zellen über `headers` und `id`.
Gilt das auch für Layout-Tabellen?
Nein – es geht um Datentabellen. Rein zur Gestaltung genutzte Tabellen sollten durch CSS ersetzt werden und brauchen nie `<th>`; die Anforderung gilt für Tabellen mit echten tabellarischen Daten.

Möchten Sie sehen, wie Ihre eigene Website abschneidet?

Kostenlosen Barrierefreiheitsscan durchführen