Data tables
Data tables with large data sets have some specific accessibility requirements.
How will a screen-reader user know the table is sortable?
Tables should have captions and we want to hook into that element to tell the user that the table is sortable.
<table>
<caption>
Past bookings<span class="visually-hidden"
>, any headings with buttons are sortable</span
>
</caption>
</table>
This will now read the semantics of the table caption
to the screen-reader.
Also, in this caption, we are telling the user that the buttons in the table headings are sortable buttons.
Sortable columns
Columns that are sortable need to inform screen-readers of that. We've already informed the user via the caption
tag but now we're going to add button
s and aria-sort
values to the th
(table headings).
<thead>
<tr>
<th aria-sort="descending" scope="col">
<button
aria-pressed="true"
type="button"
onclick="javascript:handleTableSort(event)"
>
Date
<span class="icon" aria-hidden="true"></span>
</button>
</th>
<th scope="col">Reference</th>
<th scope="col">
<button type="button" onclick="javascript:handleTableSort(event)">
Passenger
<span class="icon" aria-hidden="true"></span>
</button>
</th>
<th scope="col">Journey</th>
</tr>
</thead>
The th
have an aria-sort
value of either descending
, ascending
or nothing at all.
The button
inside the th
has an aria-pressed
attribute with a boolean value.
The icon inside the button gives a visual clue that the column is a: sortable and b: currently sorted is hidden from the screen-reader using aria-hidden="true"
(because aria-sort
should do that work for the screen-reader user, along with the caption
's message).
How does a screen-reader user know the table has been sorted?
If the table is sortable want an aria-live
region in the DOM that will tell the user when they have initiated a sorting of the table. Otherwise they may not know.
Note: the aria-live
region must be empty on first load/render. We don't want the screen-reader to read out its contents until a user interacts by clicking a table heading… so until that happens we want this element's innerText
to equal null
.
On load/first render
<span aria-live="polite" class="visually-hidden" id="feedback"></span>
After an interaction
<span aria-live="polite" class="visually-hidden" id="feedback">
The past bookings table is now sorted by date in descending order
</span>
This lets the user know what has happened (the table is now sorted by date in descending order) and to what (the past bookings table).
Full code example
<div class="table=-container">
<span aria-live="polite" class="visually-hidden" id="feedback"></span>
<table>
<caption>Past bookings <span class="visually-hidden">, any headings with buttons are sortable</span>
<thead>
<tr>
<th aria-sort="descending" scope="col">
<button aria-pressed="true" type="button" onclick="javascript:handleTableSort(event)">
Date
<span class="icon" aria-hidden="true"></span>
</button>
</th>
<th scope="col">Reference</th>
<th scope="col">
<button type="button" onclick="javascript:handleTableSort(event)">
Passenger
<span class="icon" aria-hidden="true"></span>
</button>
</th>
<th scope="col">Journey</th>
</tr>
</thead>
<tbody>
<tr onClick="javascript:handleTableRowClick(event);">
<td>28 Jun</td>
<td><a href="#">123456</a></td>
<td>Hernandez</td>
<td>London to New York</td>
</tr>
<tr onClick="javascript:handleTableRowClick(event);">
<td>29 Jun</td>
<td><a href="#">78910</a></td>
<td>Abdulameer-Jones</td>
<td>Berlin to Moscow</td>
</tr>
<tr onClick="javascript:handleTableRowClick(event);">
<td>30 Jun</td>
<td><a href="#">ABCDS</a></td>
<td>Xavier</td>
<td>Singapore to Sydney</td>
</tr>
</tbody>
</table>
</div>
<nav aria-label="Past bookings pagination" class="pagination">
<a aria-disabled="true" aria-label="Previous Page" href="#"><</a>
<a aria-current="page" aria-label="Page 1" href="#">1</a>
<a aria-label="Page 2" href="#">2</a>
<a aria-label="Page 3" href="#">3</a>
<span>…</span>
<a aria-label="Next Page" href="#">></a>
</nav>
Pagination
A table of data will often require pagination immediately after it.
<nav aria-label="Past bookings pagination" class="pagination">
<a aria-disabled="true" aria-label="Previous Page" href="#"><</a>
<a aria-current="page" aria-label="Page 1" href="#">1</a>
<a aria-label="Page 2" href="#">2</a>
<a aria-label="Page 3" href="#">3</a>
<span>…</span>
<a aria-label="Next Page" href="#">></a>
</nav>
Pagination is a list of numbered links to pages. Visually we just want to see a list of numbers and some icons that represents next/previous pages but aurally we want to hear
- Previous page, disabled
- Page 1, active page
- Page 2
- Page 3
- Next page
.visually-hidden
Above we used a CSS class on the Live region, .visually-hidden. This is a class available in a lot of CSS frameworks (sometimes called sr-only
or visuallyhidden
) and the idea is that is makes its contents visually hidden but still accessible to a screen-reader.