Data tables
Tabular data must be presented with a sound semantic and visual structure
- Version:
- 0.1.0
- Status:
- Published
Introduction
BBC tables follow longstanding conventions for the structuring of tabular data. However, some enhancements are included in the following implementation.
Since wrapping table cells, for the purposes of responsive design, would make a nonsense of the data structure, data tables must retain their rigid, two-dimensional arrangement. Accommodating tables with many columns therefore becomes a question of allowing horizontal scrolling. Since horizontal scrolling is typically avoided, and may be unexpected, it must only be applied where necessary, with both additional visual affordance (see Recommended layout) and keyboard interaction supported.
The following implementation also includes 'sticky' column header support, to help users peruse tables with a considerable number of rows, and the option to apply column sorting functionality.
Recommended markup
No matter the templating or rendering technology, data tables should be marked up with the <table>
and associated elements. Data tables composed from <div>
elements require considerable ARIA attribution to elicit the expected screen reader behaviors, and non-trivial amounts of CSS to emulate the grid-like layout behavior.
Column headers
Any <table>
that does not have column or row headers (<th>
elements) cannot be considered a true data table. In fact, some screen readers that encounter a table without headers will treat it as a 'layout table' and communicate it quite differently[1].
✕ Bad example
<table>
<tr>
<td>Fake column header 1</td>
<td>Fake column header 2</td>
<td>Fake column header 3</td>
</tr>
<tr>
<td>Row 1, cell 1</td>
<td>Row 1, cell 2</td>
<td>Row 1, cell 3</td>
</tr>
<tr>
<td>Row 2, cell 1</td>
<td>Row 3, cell 2</td>
<td>Row 4, cell 3</td>
</tr>
</table>
✓ Good example
<table>
<tr>
<th>Column header 1</th>
<th>Column header 2</th>
<th>Column header 3</th>
</tr>
<tr>
<td>Row 1, cell 1</td>
<td>Row 1, cell 2</td>
<td>Row 1, cell 3</td>
</tr>
<tr>
<td>Row 2, cell 1</td>
<td>Row 3, cell 2</td>
<td>Row 4, cell 3</td>
</tr>
</table>
Table header elements are labels for column data. With the column headers in place, when you navigate from a table cell in one column to a table cell in an adjacent column, the new column's <th>
content is announced for context. This enables users to traverse and understand tables non-visually.
Row headers
In most cases, column headers suffice. However, in some tables, the first cell of each row can be considered the 'key' cell and acts like a header for the row. It's recommended you differentiate between column and row headers explicitly using the scope
attribute:
<table>
<thead>
<tr>
<th scope="col">Column header 1</th>
<th scope="col">Column header 2</th>
<th scope="col">Column header 3</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Row 1 header</th>
<td>Row 1, cell 2</td>
<td>Row 1, cell 3</td>
</tr>
<tr>
<th scope="row">Row 2 header</th>
<td>Row 3, cell 2</td>
<td>Row 4, cell 3</td>
</tr>
</tbody>
</table>
Note that the <table>
is now divided into a head (<thead>
) and body (<tbody>
). This does not have any impact on the behavior of the table and its headers. But, as we enhance the table, these elements will come in useful for styling and scripting.