Accessible tables in HTML

by Dennis Westphal

Publication date

Tables are a good way of juxtaposing information. This allows a lot of content to be accommodated in a space-saving way. While the sighted person can grasp tabular information very quickly, it is somewhat different for me as a blind person. Simply put, a screen reader reads each table cell individually. In addition to the cell content, the cell coordinates can be read out or output on the braille display. For example, a table cell could read "0.8 column 4, row 3".

If you now imagine a large table with many values, you will understand that such an output quickly becomes confusing. The good news is that a properly structured table automatically provides a better overview. But first let's take a look at how tables are integrated into HTML in the first place.

Components

A table in HTML initially consists of three sections.

  • Header <thead> - (table header).
  • Body <tbody> - (main part of the table)
  • Footer<tfoot> - (table footer)

Within these three sections, there are again three elements.

  • Table row <tr> - (table row)
  • Table header <th>- (header cell)
  • Data cell <td>- (data cell)

Now we have a six-piece puzzle. So let's start assembling it in a meaningful way.

Structure

A table in HTML is introduced with the tag <table>. This is followed by the table header, which in turn contains a table row with the header cells. This is followed by the table body with table rows consisting of several table cells. The same happens with the following Table Footer.

It becomes clearer in practice:

<table>
<thead>
<tr><th>2020</th><th>2021</th></tr>
</thead>
</table>

In this example, a table was created with a table header, a row and two header cells. The whole thing then looks like this:

2020 2021

Two things stand out here. If one were to fill the table, there would be no allocation of data. On the left, therefore, an empty cell would have to be created in the header. And there is no content yet. I will deal with both of these in the following example. :

<thead>
<tr>
<td></td>
<th>2020</th>
<th>2021</th>
</tr>
</thead>
<tbody>
<tr>
<td>Is Christmas over already?</td>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
</table>

The result:

  2020 2021
Is Christmas over already? Yes No

Table with footer

Now you might ask yourself when this data was collected, for example. This information can be placed in the footer. This has the advantage that when longer tables are printed, this information can be seen on every page.

<table>
<thead>
<tr>
<th></th>
<th>2020</th>
<th>2021</th>
</tr>
</thead>
<tbody>
<tr>
<td>Is Christmas over already?</td>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Time of data collection</td>
<td colspan="2">The data was collected in February 2021.</td>
</tr>
</tfoot>
</table>

 

This then results in a table with header, body and footer:

  2020 2021
Is Christmas over already? Yes No
Time of data collection The data was collected in February 2021.

Accessible design

So far we have learned the general structure of a table and how to implement it in HTML. But how can tables be made accessible for screen readers?

Column headings

In the examples, it was noticeable that <th> was used in the header of the table. Screen readers use this information during navigation within the table. The larger the table, the less likely it is that you will have an overview of the table columns. This is because the screen reader reads each cell on its own. Even if there are commands to read a complete table row or column.

When I move from one column to the next, the screen reader first displays the column heading followed by the value of the table cell. This eliminates the need for me to go to the top of the table, where I would have to look up the value myself in case of doubt.

Line headings

With screen readers, I have experienced both the behaviour that the first cell of the row was read out when changing rows and that only the cell I was in was read out. To enforce a consistent behaviour here, it is recommended to fill the first column with <th> as well. Our example would therefore become the following:


<table>
<thead>
<tr>
<th></th>
<th>2020</th>
<th>2021</th>
</tr>
</thead>
<tbody>
<tr>
<th>Is Christmas over already?</th>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
<tfoot>
<tr>
<th>Time of data collection</th>
<td colspan="2">The data was collected in February 2021.</td>
</tr>
</tfoot>
</table>

For completeness, here is the table rendered again.

  2020 2021
Is christmas over already? Yes No
Time of data collection The data was collected in February 2021.

If I now change the row, the first cell of the row is output before I get the value of the cell I am on.

But that was very theoretical.

In the following screencast, I show how column and row headings work in practice and what happens if they are missing.

Screencast: Tabellen im Screenreader

Again in a nutshell

Screen readers read each cell individually. Column and row headings can be used to make orientation within a table extremely easy. A table that doesn't have that looks something like this table does to sighted people: