Search code examples
htmlhtml-tableaccessibilitysemantic-markupwcag2.0

How to markup data cell that also functions as row header in HTML5?


I'm trying to create a semantically correct and accessible (WCAG 2.0) HTML5 data table. The first column contains data which also functions as a header for the row.

In HTML 4.01 / XHTML 1.0 I used to markup these cells as td with a scope attribute:

<td scope="row">

In HTML5 the scope attribute is no longer allowed on the td element. What would be the best approach for semantics and accessibility?

  1. markup the cells in the first column as th (the data aspect is lost?)
  2. markup the cells in the first column as td and loose the header aspect

I'm afraid that in the second scenario the table will be less accessible.

This situation got me thinking about data tables in general: in which situation would you use ONLY column headers? Because the nature of a data table implies a relationship between the data in the cells in a row. For accessibility, shouldn't the rows always get a header as well, to convey that relationship?

For example this table:

Principle                                    | Corresponding guidelines
------------------------------------------------------------------------
Never leave a child unattended               | 1, 3, 8
Always communicate what you are going to do  | 4, 5, 6
Calm down before you take action             | 5,9,13

Would you consider the cells in the first column (the principles) to be headers for the second column, or data cells? In other words: in what way would you markup this table?


Solution

  • Just because a cell is a TH, does not mean it is not also data. You should use TH with scope="row". In HTML4, you could place a scope attribute on a TD cell, in HTML5 it is no longer acceptable to have a scope attribute an a TD cell.

    <table>
        <thead>
            <tr>
                <th scope="col">Principle</th>
                <th scope="col">Corresponding Guidelines</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th scope="row">Never leave a child unattended</th>
                <td>...</td>
            </tr>
        </tbody>
    </table>
    

    In general most data tables have a cell that could be considered a header cell. I have seen exceptions to that rule - in particular for automatically generated data (like for example log file data) where every element in the row is of equal importance and nothing can really be considered a header.

    In these instances, you could decide to dynamically set the header based on the sorting criteria but this is difficult to get to work across assistive technologies. If you were to do that, you would want to use ARIA grid markup with aria-labelledby to point to the headers. You should probably also test that solution for usability.