Tables in accessibility

Associated themes:
  • Web
  • Beginner

Publication date

Update of the article from

General presentation #

A table is an arrangement of information in rows and columns containing cells that make it easy to compare and highlight information. It allows tabular data to be presented in a two-dimensional grid, such data is easier to read in tabular form.

This allows a sighted user to quickly make visual associations between table data and its table headings.

However, a blind user will not have access to all these relations between the information, it's the reason why it is important that a table is implemented with the appropriate HTML markup so that it is correctly rendered by assistive technologies.

In the rest of this article, we will see the main rules to follow to create an accessible table.

Add a caption/title to your table #

It is important to define a title for your table. This text must be concise and relevant, so that the nature and type of data it contains can be understood.
It must be associated with the table using the caption element and must be the first element after the opening table tag.

Example using caption #


<table>
    <caption>2022 timetable</caption>
    [...]
</table>

Another way to provide a title is to use the aria-labelledby or aria-label attributes on the table tag. However, a title must be visible.

Example using aria-label #


<table aria-label="table title">
    [...]
</table>

As a last resort, it is also possible to use the title attribute, but a title must be visible.

Example using title #


<h2 id="title"> 2022 Schedule</h2>
<table title="title">
    [...]
</table>

Complex table: add a description #

In the case of a complex table, the title must be accompanied by a summary describing the spatial organization of the data.
It is recommended to use the ARIA aria-describedby attribute, which allows the description to be programmatically linked to the table.


<p id="info-table">
example of description to help understanding complex table
</p>

<table aria-describedby="info-table">
    <caption>Time planning 2022</caption>
    [...]
</table>

Identify your table headers #

Simple tables: scope attribute #

To help assistive technology users, you must identify table headings, whether for rows or columns.
To identify these table headers, you must use the th tag, which must never be empty.

Once the headers are created, the data cells must be associated with the headers on which they rely.
The scope attribute allows cells to be programmatically linked to headers, and therefore, to be identified by assistive technologies.

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

Complex tables: id and header attributes #

Some tables are too complex to identify a strict horizontal or vertical association (for example, merged columns or rows) between the header and the data cells.
The scope attribute does not solve this problem. A unique id attribute must be used for each header cell. To link this header to a cell, you must use the headers attribute and adding the required id.

For example, we have two header cells, <th id="toto">Toto</th> and <th id="tata">Tata</ th>, the code to link it to a data cell will be <td headers="toto tata">Tota</td>.

Tables should use headers/id only if:

  • The table has column/row headings that change within the table.
  • A data cell with three or more related headers (often linked to header cells that are merged)

Special cases for formatting tables #

Where possible, avoid using tables for layout. Tables are primarily designed to present tabular data, and CSS styles allow you to avoid using tables for formatting.

However, if you do use a table for layout, you must follow these rules:

  • the table element must have the attribute role="presentation" or role="none" in order to remove the semantics from the table
  • semantic elements specific to a table must not be used: caption, th, scope, headers,
  • Ensure that if there is a specific reading order for understanding the content, this order is respected when reading the table linearly (which follows the order of appearance in the code).

Creating accessible tables will allow consistent reading of these tabular data with a screen reader. To navigate in a table with Jaws or NVDA, there are several specific shortcuts.

NVDA #

To quickly navigate from table to table in a page, just use the t key. If you use the Shift + t shortcut, you will navigate in the opposite direction and return to the previous table.

Once inside a table, there are several shortcuts to move around easily:

  • Ctrl + Alt + left arrow moves to the left column while keeping the same line, Ctrl + Alt + right arrow moves to the column right.
  • Ctrl + Alt + down arrow moves to the next line while staying on the same column, Ctrl + Alt + up arrow moves to the previous line.

Jaws #

For Jaws, you have to use the Y key and Shift + Y to navigate between tables.
To browse in tables, there are several shortcuts:

  • Insert + Ctrl + t lists all tables
  • Ctrl + Alt + left arrow moves to the left column while keeping the same line, Ctrl + Alt + right arrow moves to the column right.
  • Ctrl + Alt + down arrow moves to the next line while staying on the same column, Ctrl + Alt + up arrow moves to the previous line.
  • Insert + Shift + up arrow reads the entire line.
  • Insert + Shift + 5 reads the entire column.

Table examples #

We will now show you examples of accessible tables.

Simple table #

The first example is a table with only headers on the columns, so we use the scope="col" attribute for assistive technologies to interpret it correctly.

People with their professional activity
First name Last Name Gender Job
John Doe M Unknown
Marty McFly M Guitarist
Ellen Ripley F Astronaut
Indiana Jones M Archaeologist
Sarah Connors F Waitress

<table class="table">
 <caption class="h4"> People with their professional activity</caption>
  <tr>
    <th scope="col">First name</th>
    <th scope="col">Last Name</th>
    <th scope="col">Genre</th>
    <th scope="col">Job</th>
  </tr>
  <tr>
    <td>John</td>
    <td>Doe</td>
    <td>M</td>
    <td>Unknown</td>
  </tr>
  [...]

Like this, it is possible to easily navigate within the table using a screen reader. Also, any cell change from one column, or row, to another, the header will be vocalized.

For example, if we are positioned on the First name column, and we use the shortcut Ctrl+Alt+right arrow to go to the "Last name" column, NVDA vocalizes "Last Name Column 2 + column text ".

Tables with two headers #

In this second example, the table is a planning time allowing you to quickly know whether the store is open or not, depending on the day of the week and the time.

This table requires two headers, one for the days of the week and another for the time slot.

Toy store opening
Monday Tuesday Wednesday Thursday Friday
09:00 - 11:00 Closed Open Open Open Closed
11:00 - 13:00 Open Open Open Open Closed
13:00 - 15:00 Open Open Closed Open Open
15:00 - 17:00 Open Open Closed Open Open

<table class="table">
  <caption class="h4">Toy store opening</caption>
   <tr>
     <td></td>
     <th scope="col">Monday</th>
     <th scope="col">Tuesday</th>
     <th scope="col">Wednesday</th>
     <th scope="col">Thursday</th>
     <th scope="col">Friday</th>
   </tr>
   <tr>
     <th scope="row">09:00 - 11:00</th>
     <td>Closed</td>
     <td>Open</td>
     <td>Open</td>
     <td>Closed</td>
     <td>Closed</td>
   </tr>
   [...]

Complex table #

In this example, some given cells have three associated headers, so we have to use the id and headers attributes.

Since the table is complex, we can add a description to help users understand the table nature an structure.

Tables for calculating the compliance rate of a website.
For each page the criteria can be compliant, not compliant or not applicable, and have two levels of difficulty: Beginner or Advanced

Summary by level
Criteria Compliant Not Compliant Not Applicable Compliance rate
Level Beginner Advanced Beginner Advanced Beginner Advanced
Home 17 13 0 0 13 7 100%
Article 17 12 0 1 13 7 97%

<p class="border-top border-light" id="tblDesc">Description of the table</p>
<table aria-describedby="tblDesc" class="table">
 <caption class="visually-hidden position-relative">Summary by level</caption>
 <tr>
    <th id="criterion">Criteria</th>
    <th id="compliant" headers="criterion" colspan="2">Compliant</th>
    [...]
  </tr>
  <tr>
    <th id="level">Level</th>
    <th id="beginner-compliant" headers="Compliant">Beginner</th>
    <th id="advanced-compliant" headers="Advanced">Advanced</th>
    [...]
  </tr>
  <tr>
    <th id="home">Home</th>
    <td headers="home compliant beginner-compliant">17</td>
    <td headers="home compliant advanced-compliant">13</td>
    [...]
  </tr>
  [...]