EPUB 3 Accessibility Guidelines

Tables

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. Readers 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 reader 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 reader, so care must be taken to ensure logical playback results.

The ordering does not have to exactly match the markup if another would makes more sense. In the previous example. it will make more sense to the reader 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 (e.g., headers at the end of a row, mixed heading rows and columns, etc.). (See Example 3.)

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>

Note that HTML5 no longer allows the td element to be used for headings; td elements also cannot be used in thead rows.

Captions and summaries

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

For complex tables, including a summary of the structure for the reader before they enter the content can greatly assist comprehension, as well. HTML5 currently does not provide a mechanism to include this information in a programmatically deterministic way (i.e., so that someone can discover the information without hunting around manually for it).

Summaries of complex tables can be included in a details element in the table's caption. Although not a perfect solution, any reader will be able to find the summary, if it exists, when they encounter the caption. (Note: the details element has been marked as at risk for removal from HTML5.)

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

Tables should never be used to lay out documents. See the section on fixed layout positioning with CSS for more information on how to create these documents more accessibly.

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.

Examples

Example 1 — Multi-row header

The following table header

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

becomes:

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

The following table shows a distance chart with the destinations defined at the end of the 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

which would be marked up as:

<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 — Multidirectional headings

The following table with headers at 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%

would be marked up as:

<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 — Including a table summary

A summary could be added to Example 3 as follows

<table border="1">
   <caption>Table IX.4 Income Distribution Among Families 1929-1997
       <details>
          <summary>Summary</summary>
          <p>
             The table breaks down the data across
             two dimensions. Each column in the 
             table defines the year of the sampling 
             period and each row an socio-economic 
             bracket. …
          </p>
       </details>
   </caption>
   …
</table>

Compliance References and Standards

Additional Resources

The following resources present HTML 4 techniques that may not all still be applicable:

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 reading systems, as most reading systems 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 readers in trying to aid accessible devices.

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

Some reading systems render pop-out content in browser windows, which can also improve reading for sighted readers 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.