Home / Knowledge Base / Non-Empty Elements in Large Tables Require Table Headers

Non-Empty Elements in Large Tables Require Table Headers

Data tables are essential for organising complex information, but if table headers (<th>) and data cells (<td>) are not properly connected, users who rely on screen readers cannot understand the relationships between table content. Proper markup ensures that all users, including those using assistive technologies, can interpret and navigate tables effectively.

How Screen Readers Navigate Tables

Screen reader users move through tables using keyboard commands to go cell by cell, row by row, or column by column. As they enter each data cell, the screen reader announces the related headers first, followed by the cell’s value. This allows users to understand where they are in the table and how each piece of data relates to the headers. When headers are missing or not correctly linked, this navigation breaks down, and the data loses its meaning.

WCAG Success Criterion

1.3.1 Info and Relationships – Level A
Information, structure, and relationships conveyed through presentation must be programmatically determined or available in text.

This means that table headers must be correctly associated with their data cells, allowing assistive technologies to identify which headers describe which pieces of data.

Why This Matters

Screen readers read tables one cell at a time as users move through them using keyboard commands. When table headers are correctly marked up, the screen reader announces the column header first, then the row header (if present), followed by the cell value.

For example, in a results table, a screen reader might announce: “Distance, 5 kilometres. Time, 28 minutes 4 seconds.”

Without proper header associations, the screen reader may announce only the value, such as “28:04,” without explaining what it represents. This means users hear the data but not the context, making the table difficult or impossible to understand.

Accurate table structure helps ensure that:

  • Users who are blind or visually impaired can understand how data relates across rows and columns.
  • Screen readers can announce table information clearly and logically.
  • Users can navigate efficiently by headers, improving comprehension and usability.

When You Should Use It

Whenever you display structured data, use proper HTML table elements so that assistive technologies can understand the table correctly. This means using <table> for the table itself, <th> for header cells, and <td> for data cells, and making sure headers are clearly connected to the data they describe, mainly for:

  • Performance statistics or records
  • Reports, schedules, or comparisons
  • Financial data or analytics
  • dashboards

For decorative or layout-only tables, consider alternative layouts; however, for data tables, semantic markup is essential.

How to Fix the Problem

To fix table accessibility issues, make sure assistive technologies can programmatically determine which headers describe each data cell. In simple terms, a screen reader must be able to tell users what row and column a value belongs to.

WCAG does not define tables as “small” or “large” based on size, and screen readers do not change behaviour based on the number of rows or columns. Header associations are required whenever a table presents data, regardless of its size or complexity.

Example 1: Simple Data Table Using scope

For straightforward tables with clear rows and columns, use <th> for headers and the scope attribute to define whether a header applies to a column or a row.

Key idea

  • <th scope="col"> → header for a column
  • <th scope="row"> → header for a row

Table Example

Greensprings Running Club – Personal Bests

Name1 mile5 km10 km
Mary8:3228:041:01:16
Betsy7:4326:4755:38
Matt7:5527:2957:04
Todd7:0124:2150:35

HTML Code

<table class="data">
  <caption>Greensprings Running Club Personal Bests</caption>
  <thead>
    <tr>
      <th scope="col">Name</th>
      <th scope="col">1 mile</th>
      <th scope="col">5 km</th>
      <th scope="col">10 km</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Mary</th>
      <td>8:32</td>
      <td>28:04</td>
      <td>1:01:16</td>
    </tr>
    <tr>
      <th scope="row">Betsy</th>
      <td>7:43</td>
      <td>26:47</td>
      <td>55:38</td>
    </tr>
    <tr>
      <th scope="row">Matt</th>
      <td>7:55</td>
      <td>27:29</td>
      <td>57:04</td>
    </tr>
    <tr>
      <th scope="row">Todd</th>
      <td>7:01</td>
      <td>24:21</td>
      <td>50:35</td>
    </tr>
  </tbody>
</table>

Tip: Use CSS for borders, colours, and alignment. Avoid HTML presentation attributes.

Example 2: Complex Table Using id and headers

Some tables have multiple layers of headers (for example, grouped columns). In these cases, scope alone may not be enough. You can explicitly link each data cell to all relevant headers using the id and headers attributes.

Important clarification

  • The attribute is called headers (plural) because a data cell may be associated with more than one header.

When to use this method

  • Column groups
  • Multi-level headers
  • Data that depends on more than one category

Updated Example Table (Concept)

Personal Bests by Gender and Distance

HTML Code (Modernised)

<table class="data complex">
  <caption>Personal Bests by Gender and Distance</caption>

  <thead>
    <tr>
      <th></th>
      <th colspan="2" id="females">Females</th>
      <th colspan="2" id="males">Males</th>
    </tr>
    <tr>
      <th scope="col">Distance</th>
      <th id="mary">Mary</th>
      <th id="betsy">Betsy</th>
      <th id="matt">Matt</th>
      <th id="todd">Todd</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <th id="mile">1 mile</th>
      <td headers="females mary mile">8:32</td>
      <td headers="females betsy mile">7:43</td>
      <td headers="males matt mile">7:55</td>
      <td headers="males todd mile">7:01</td>
    </tr>
    <tr>
      <th id="km5">5 km</th>
      <td headers="females mary km5">28:04</td>
      <td headers="females betsy km5">26:47</td>
      <td headers="males matt km5">27:29</td>
      <td headers="males todd km5">24:21</td>
    </tr>
    <tr>
      <th id="km10">10 km</th>
      <td headers="females mary km10">1:01:16</td>
      <td headers="females betsy km10">55:38</td>
      <td headers="males matt km10">57:04</td>
      <td headers="males todd km10">50:35</td>
    </tr>
  </tbody>
</table>

Notes

  • Avoid obsolete attributes like border="1".
  • Avoid placeholder hacks such as hidden “empty” spans.
  • Always use <thead> and <tbody> for consistency and clarity.
  • The id + headers method is more complex and should be used only when needed.
  • Always test with real screen readers, as support can vary.

Common Mistakes

  • Using <td> instead of <th> for header cells.
  • Missing or incorrect scope attributes.
  • Referencing non-existent IDs in headers.
  • Using tables for layout rather than data.

Good Practices

  • Always use semantic HTML elements (<table>, <thead>, <tbody>, <th>, <td>).
  • Include a <caption> to describe the table’s purpose.
  • Use scope for simple tables and id + headers for complex ones.
  • Test with a screen reader to ensure headers are read correctly.
  • Consider simplifying complex data into smaller tables for better accessibility.

Wrapping Up

Proper table header associations make data tables accessible, logical, and easy to navigate.
When headers and data are correctly linked, screen reader users can understand and interact with the information as clearly as sighted users.

Want to test against all WCAG success criteria?

Stay compliant. Avoid fines. WebYes reviews your entire website so you don't have to worry.

Sign Up for Free Now