Summary

The correct tagging and description of data tables simplifies access and understanding for users who cannot see the data or need additional help understanding it.

Techniques

Examples

Example 1 — Irregular header

The following table has headers that span columns and rows:

Shipping. Stock. Wages. Weights. Name of Colony.
Book, page. Appx, page. Book, page. Appx, page. Book, page. Appx, page.

To make these headings accessible, use colgroup elements with the scope attribute:

<table border="1">
   <colgroup span="2"/>
   <colgroup span="1"/>
   <colgroup span="2"/>
   <colgroup span="2"/>
   <colgroup span="1"/>
   <thead>
     <tr>
       <th id="ship" colspan="2" scope="colgroup">Shipping.</th>
       <th id="stock" rowspan="2" scope="colgroup">Stock.</th>
       <th id="wages" colspan="2" scope="colgroup">Wages.</th>
       <th id="wt" colspan="2" scope="colgroup">Weights.</th>
       <th id="name" rowspan="2" scope="colgroup">Name of Colony.</th>
     </tr>
     <tr>
       <th scope="col">Book, page.</th>
       <th scope="col">Appx, page.</th>
       <th scope="col">Book, page.</th>
       <th scope="col">Appx, page.</th>
       <th scope="col">Book, page.</th>
       <th scope="col">Appx, page.</th>
     </tr>
   </thead>
   …
</table>
Example 2 — Complex headings

The following table shows a distance chart with the start destinations defined in the first row and at the end destination at the end of each subsequent row:

Vancouver Calgary Saskatoon Winnipeg Toronto Montreal St. John's
7323 6334 5838 5010 3141 2602 St. John's
4271 3743 3232 2408 539 2602 Montreal

Use scope="col" to make the start destinations the column headers and scope="row" to make the end destinations the row headers:

<table border="1">
   <tr>
     <th scope="col">Vancouver</th>
     <th scope="col">Calgary</th>
     <th scope="col">Saskaton</th>
     <th scope="col">Winnipeg</th>
     <th scope="col">Toronto</th>
     <th scope="col">Montreal</th>
     <th scope="col">St. John's</th>
     <td></td>
   </tr>
   <tr>
     <td class="center">7323</td>
     <td class="center">6334</td>
     <td class="center">5838</td>
     <td class="center">5010</td>
     <td class="center">3141</td>
     <td class="center">2602</td>
     <td class="center"></td>
     <th scope="row">St. John's</th>
   </tr>
   <tr>
     <td class="center">4271</td>
     <td class="center">3743</td>
     <td class="center">3232</td>
     <td class="center">2408</td>
     <td class="center">539</td>
     <td class="center"></td>
     <td class="center">2602</td>
     <th scope="row">Montreal</th>
   </tr>
</table>
Example 3 — Layered headings

The following table combines headers from the top of each column and beginning of each row:

Table IX.4 Income Distribution Among Families 1929-1997
% Families 1929 1970 1997
Lowest 20% 3.5% 3.5% 5.5% 5.5% 4.2% 4.2%

The headers attribute is used to provide the IDs of the cells that contain the relevant heading text:

<table border="1">
   <caption>Table IX.4 Income Distribution Among Families 1929-1997</caption>
   <tr>
     <th id="t4-pct">% Families</th>
     <th id="t4-1929" colspan="2">1929</th>
     <th id="t4-1970" colspan="2">1970</th>
     <th id="t4-1997" colspan="2">1997</th>
   </tr>
   <tr>
     <th id="t4-low20">Lowest 20%</th>
     <td headers="t4-1929 t4-low20">3.5%</td>
     <td headers="t4-1929 t4-low20">3.5%</td>
     <td headers="t4-1970 t4-low20">5.5%</td>
     <td headers="t4-1970 t4-low20">5.5%</td>
     <td headers="t4-1997 t4-low20">4.2%</td>
     <td headers="t4-1997 t4-low20">4.2%</td>
   </tr>
   …
</table>

Excerpt from: Basic Microeconomics — R. Larry Reynolds

Example 4 — Table with footnotes

Footnotes are included after the table, not in the tfoot. Both table and notes can be grouped within a figure:

<figure>
   <table>
     <tr>
       <th>Population</th>
       <th>Birth Rate<a epub:type="noteref" href="#tblnote01">*</a></th>
     …
   </table>
   <aside epub:type="footnote">
     <p>* Data not available for all years</p>
   </aside>
</figure>

Explanation

When tables are not structured accessibly, the data they contain can quickly become a meaningless sea of numbers, facts and figures to someone moving through them a cell at a time. Users who cannot see the table cannot use visual cheats like checking the alignment and scanning back to the top headings to orient themselves as they go. Equivalent information needs to be encoded into the table to facilitate comprehension.

Headers

One of the primary aids for table navigation is the proper use of headers. Correctly identified and linked headers provide metadata the user can call up as needed as they navigate the data points.

The simplest kinds of tables contain a single header for each column or row, and are typically handled by assistive technologies (AT) without the need for extra information. The header cells need only be marked using the th element and the AT will be able to replay them when requested:

<table border="1">
   <tr>
     <th>Heading 1</th>
     <th>Heading 2</th>
     <th>Heading 3</th>
   </tr>
   <tr>
     <td></td>
     <td></td>
     <td></td>
   </tr>
  …
</table>

If a table's header spans more than one row, a thead element should be used to group all the header rows (see Example 1). When more than one header applies to a column, a headers attribute should be attached to each of its cells to clarify for an AT which headers apply. For example:

<table border="1">
   <thead>
     <tr>
       <th id="sales" rowspan="4">Sales</th>
     </tr>
     <tr>
       <th id="ub" rowspan="2">Ultrabooks</th>
       <th id="tab" rowspan="2">Tablets</th>
     </tr>
     <tr>
       <th id="ugross">Gross</th>
       <th id="unet">Net</th>
       <th id="tgross">Gross</th>
       <th id="tnet">Net</th>
     </tr>
   </thead>
   <tbody>
     <tr>
       <td headers="ugross sales ub">$100,000</td>
       <td headers="unet sales ub">$12,500</td>
       <td headers="tgross sales tab">$45,000</td>
       <td headers="tnet sales tab">$5,250</td>
     </tr>
   </thead>
   …
</table>

The headers attribute identifies the cells that contain the header text by their id attribute value. The order in which the ids are included in the attribute determines how the headings are to be announced or rendered to the user, so care must be taken to ensure logical playback results.

The ordering of the headers does not have to exactly match the markup if another ordering would make more sense. In the previous example. it will make more sense to the user to announce "gross sales ultrabooks" than "sales ultrabooks gross".

More generally, whenever there is any ambiguity about which headings apply to a cell, a headers attribute should be attached. Note that HTML5 no longer allows the td element to be used for headings (i.e., a td element cannot be referenced from the headers attribute).

The scope attribute can also be used to clarify relationships between headers and cells. Whenever the headers are not in the first row or column of a table, this attribute should be attached to the th elements to clarify their directionality (to the row or column they belong to). headers attributes are not needed when the scope is clearly defined.

<table border="1">
   <tr>
     <td>1</td>
     <td>2</td>
     <td>3</td>
     <td>4</td>
     <th scope="row">Heading</th>
   </tr>
</table>

Captions and summaries

A caption should always be included to give context to a table. Never rely on the surrounding text to explain the presence of a table, especially when the table is offset from the content.

For complex tables, including a summary of the structure for the user before they enter the content can greatly assist comprehension, as well. The HTML5 specification defines some techniques for describing tables.

Secondary content

If a table is not required to be read at the point of insertion (i.e., it is not part of the logical reading order), use a figure tag to enclose it.

Presentational layout

Avoid using tables for layout purposes. If a presentational table layout is unavoidable, consider using the CSS table layout properties to achieve the effect.

When using a table for presentational purposes, the table must not include any data table elements or attributes. These include: the thead, th and caption elements, and headers and scope attributes (also the summary attribute if creating HTML4 or XHTML 1.1 documents).

Also avoid using tables when all that the cells contain are lists of information (e.g., for placing information side-by-side, or where the first column in each row is a new unrelated header and the second a list of relevant to that topic). The extra table formatting is an unnecessary encumbrance to navigation.

References

Frequently Asked Questions

What about using pictures of tables?

Although table rendering is still problematic in ebooks, including images of tables instead of the actual data takes the content away from anyone who cannot see it.

Tables are a problem that still need solving in user agents, as most user agents aren't able to provide extended horizontal or vertical scrolling to facilitate rendering. As a result, large tables get rendered in the available space – leaving rows and columns to spill across page views – compromising the quality of the ebook for sighted users.

If you are going to include an image of a table, consider providing a link to a file containing the table markup. Users with TTS playback available will be able to navigate the markup regardless of the rendering quality.

Some user agents render pop-out content in browser windows, which can also improve reading for sighted users unable to read images that render too small or blow out of the viewport.

See also this CSS-Tricks article on responsive tables for an alternate approach to visual table rendering based on the available screen size.