Improving the accessibility of tables

Table of Contents

Tables are frequently used in Web pages and applications to format information. The problem is that a client using a screen reader can easily be confused if tables are not used properly. Avoid nested tables whenever possible since they are very difficult to understand using a screen reader.

Tables should only be used to display data that needs to be divided into rows and columns. If it makes sense to present the data using a spreadsheet, then it will likely be appropriate to present the same data using a table.

Tables must make sense when linearised (read from left to right like a paragraph). Here is an example of a 3 row by 3 column table that would not make sense when linearised:

200 
------=2
100 
  

A screen reader would read this table as follows:
"200" "------" "=" "2" "100"

The data should be represented in one cell by "200/100 = 2" or "200 divided by 100 equals 2".

It is important to close all <td>, <th> and <tr> tags properly; otherwise, formatting problems may occur outside of the table in certain browsers.

How to make data tables accessible

Caption (optional)

A caption is optional and is used to provide the title for a table. This title will be centered immediately above the top of the table. Styles may be used to change the caption alignment. The following is an example of how a caption would be used:

<table>
<caption>Cups of coffee consumed by each delegate</caption>

Associating individual data cells with their respective row and column headers

It is important to associate data cells and header cells. This can be done using the scope attribute or the id and headers attributes. This allows clients using screen readers the option of reading the contents of the associated header cells before reading the content of each data cell. This will make it easier for the client to understand the meaning of the data.

An example would be a table containing multiple columns and rows where there is one column with the header "Dollars earned" and with two data cells several rows below containing "345" and "567". Without the use of the scope attribute or the id and headers attributes the screen reader would read:

"Dollars earned" / ... multiple cells ... / "345" / ... multiple cells ... / "567"

In this case, a blind user would have difficulty understanding the meaning of "345" and "567". If the scope attribute or the id and headers attributes were used then the screen reader would read:
"Dollars earned" / ... multiple cells ... / "Dollars earned 345" / ... multiple cells ... / "Dollars earned 567"

In this case, it is clear what the meaning of "345" and "567" is since for each cell the header is read before the data.

The scope attribute is used to declare the header cell for a row or a column. This attribute can only be used for simple tables where there is no more than one header row or column.

The id attribute is used to declare a header cell while the headers attribute is used to indicate what headers the data cell is associated with. The id and headers attributes can be used for any data table. The variable used in id must be formed of only numbers, letters or dashes. It must not contain any spaces and the first character must be a letter. Once a variable has been used for an id attribute, it must not be used again by another id attribute for the remainder of the page.

There are three categories of cells: header cells, sub-header cells and data cells.

Header cell

This cell is the column or row header for one or more data cells and is not the sub-header for any other header cell. If there are no sub-headers then the scope attribute can be used. If there are sub-headers then the id attribute must be used. The th element must be used for header cells.

  • Example of a header cell using scope:

    <th scope="col">Name of Delegate</th>

  • Example of a header cell using id:

    <th id="header1">Name of Delegate</th>

Sub-header cell

This cell is the column or row header for one or more data cells and is the sub-header for one or more header cells. In this case, both the id and headers attributes will be used. The th element must be used for sub-header cells.

  • Example of a sub-header cell:

    <th id="header1a" headers="header1">First Name</th>

  • Example of a sub-header cell associated with more than one header (note that the headers are separated by a single space):

    <th id="header1a" headers="header1 header2">First Name</th>

Data cell

This cell is not a column or row header. The td element should be the only element used for data cells.

  • Example of a data cell where the header cell uses the scope attribute:

    <td>Jim Simpson</td>

  • Example of a data cell where the header cell uses the id attribute:

    <td headers="header1">Jim Simpson</td>

  • Example of a data cell associated with more than one header (note that the headers are separated by a single space):

    <td headers="header1 header2">Jim Simpson</td>

Examples of accessible tables

Table with a caption and column headers using the scope attribut

<table>
<caption>Cups of coffee consumed by each delegate</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Cups</th>
<th scope="col">Type of Coffee</th>
<th scope="col">Sugar?</th>
</tr>
</thead>
<tbody>
<tr>
<td>T. Sexton</td>
<td>10</td>
<td>Espresso</td>
<td>No</td>
</tr>
<tr>
<td>J. Dinnen</td>
<td>5</td>
<td>Decaf</td>
<td>Yes</td>
</tr>
</tbody>
</table>

Cups of coffee consumed by each delegate
Name Cups Type of Coffee Sugar?
T. Sexton 10 Espresso No
J. Dinnen 5 Decaf Yes

Table with a caption and column headers using the id attribute

<table>
<caption>Cups of coffee consumed by each delegate</caption>
<thead>
<tr>
<th id="header1">Name</th>
<th id="header2">Cups</th>
<th id="header3">Type of Coffee</th>
<th id="header4">Sugar?</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="header1">T. Sexton</td>
<td headers="header2">10</td>
<td headers="header3">Espresso</td>
<td headers="header4">No</td>
</tr>
<tr>
<td headers="header1">J. Dinnen</td>
<td headers="header2">5</td>
<td headers="header3">Decaf</td>
<td headers="header4">Yes</td>
</tr>
</tbody>
</table>

Cups of coffee consumed by each delegate
Name Cups Type of Coffee Sugar?
T. Sexton 10 Espresso No
J. Dinnen 5 Decaf Yes

Table with a caption, column headers and column sub-headers

<table>
<caption>Cups of coffee consumed by each delegate</caption>
<thead>
<tr>
<th id="header5" colspan="2">Delegate</th>
<th id="header6" rowspan="2">Cups</th>
<th id="header7" rowspan="2">Type of Coffee</th>
<th id="header8" rowspan="2">Sugar?</th>
</tr>
<tr>
<th id="header5a" headers="header5">Name</th>
<th id="header5b" headers="header5">State</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="header5 header5a">T. Sexton</td>
<td headers="header5 header5b">Delaware</td>
<td headers="header6">10</td>
<td headers="header7">Espresso</td>
<td headers="header8">No</td>
</tr>
<tr>
<td headers="header5 header5a">J. Dinnen</td>
<td headers="header5 header5b">New York</td>
<td headers="header6">5</td>
<td headers="header7">Decaf</td>
<td headers="header8">Yes</td>
</tr>
</tbody>
</table>

Cups of coffee consumed by each delegate
Delegate Cups Type of Coffee Sugar?
Name State
T. Sexton Delaware 10 Espresso No
J. Dinnen New York 5 Decaf Yes

Table with row and column headers

<table>
<thead>
<tr>
<td></td>
<th id="header9">2002</th>
<th id="header10">2003</th>
</tr>
</thead>
<tbody>
<tr>
<th id="header11" class="alignLeft">Cars</th>
<td headers="header9 header11">54678</td>
<td headers="header10 header11">57765</td>
</tr>
<tr>
<th id="header12" class="alignLeft">Vans</th>
<td headers="header9 header12">34546</td>
<td headers="header10 header12">45645</td>
</tr>
<tr>
<th id="header13" class="alignLeft">Trucks</th>
<td headers="header9 header13">76565</td>
<td headers="header10 header13">64574</td>
</tr>
</tbody>
</table>

2002 2003
Cars 54678 57765
Vans 34546 45645
Trucks 76565 64574
Date modified: