Accessibility Tutorial
Developer's Guide Part 3
Data Tables
As we said above, it is strongly advised not to use tables for layout, even though screen readers can get through them if the nesting is not too deep. But if you are using tables for their intended purpose, it is essential to know how to mark them up so that screen readers can correctly convey data to a user with a visual disability.
Caption and Summary
The CAPTION element is meant to provide a brief caption for all users. It generally appears above the data table, but can easily be moved with CSS using the caption-side property. The CAPTION element appears just after the opening TABLE element. The summary attribute, on the other hand, is used with the TABLE element, and provides more detailed information about the table. Screen readers may read the summary text while all visual browsers ignore it. With this analogy, summary text might be likened to title text.
<table summary="All state capitals with other pertinent data including population,
state flower, and total square miles">
<caption>State capitals and other fun facts</caption>
...
</table>
THEAD, TBODY, TFOOT
The THEAD attribute, as its name implies, is generally to be used around the first row that contains the TH elements. Each successive row of data cells would then be bounded by the TBODY element. A very long table might repeat the headings at the bottom of the table to help the sighted user. (Yes, accessibility is for the sighted user too). This bottom row would use the TFOOT element. Simple and short tables do not need these added elements. (See the example under the next section. These attributes also help when a data table is printed.)
Scope
The scope attribute is used for the TH and TD elements. It indicates whether the contents of an element pertain to the column beneath the element, or to the row that it is in. Acceptable values are "col" and "row". Generally the scope attribute is not needed if the data table is simple and the data is displayed in straightforward columns. However, it is best to use the scope attribute to be safe.
Here is an example using the scope attribute to associate data in more complex ways. Note that a TD element can have a dual role. That is, it can be a data cell that also in turn acts as a table header (TH).
<thead>
<tr>
<td></td>
<th scope="col">Animal</th>
<th scope="col">Vegetable</th>
<th scope="col">Mineral</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Edible</th>
<td>Yes</td>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
The headings "Animal", "Vegetable", and "Mineral" refer to their respective columns, while their fitness for eating refers to the row. Note that it is legal to have a TH element within the same table row as a TD element. The TH element in the second row is acting as a heading, and is thus semantically correct.
The ABBR AttributeTo make things a little more user-friendly for users of screen readers, the abbr attribute can be used on the TH element so that each successive reading of the heading will be shortened, and therefore less irksome. Consider the following example. When the TH is read the first time, the screen reader will say "Metropolitan Museum of Art". For each successive row it will say "MMA".
<th abbr="MMA">Metropolitan Museum of Art</td>
Explicit Data Association: the Headers and ID Attributes
It is best to avoid complex data tables because screen readers have difficulty with them. But if you are intent on using data tables that contain two or more logical levels of row or column headers, other attributes should be used. TH elements (and TD elements acting as headers) can use the id attribute, while all data cells associated with them can use the corresponding headers attribute. These attributes do not work unless they are both present. Consider the following complex data table.
| Type | Cost | Tax | Total |
|---|---|---|---|
| In Store Milwaukee | 100.00 | 8.00 | 108.00 |
| In Store Chicago | 120.00 | 10.00 | 130.00 |
| On Web | 90.00 | 0.00 | 9.00 |
| On Web Savings vs. Milwaukee | 10.00 | 8.00 | 18.00 |
| On Web Savings vs. Chicago | 30.00 | 10.00 | 40.00 |
The complexity of this table lies in multiple associations. For instance see the bottom row, third column. The text "10.00" is associated with "Tax", but also with "On Web Savings vs. Chicago" and "In Store Chicago". Therefore its headers attribute is associated with the third and sixth rows, but also the third column. The scope attribute could of itself associate that cell with the sixth row and third column, but not with the third row. Here is the complete HTML table.
<table summary="A cost comparison of a purchase made in store in Milwaukee versus
Chicago versus on the web, including tax and total">
<caption>Cost comparison store vs. web purchase</caption>
<thead>
<tr>
<th id="col1">Type</th>
<th id="col2">Cost</th>
<th id="col3">Tax</th>
<th id="col4">Total</th>
</tr>
</thead>
<tbody>
<tr>
<th id="row2" headers="col1">In Store Milwaukee</th>
<td headers="row2 col2">100.00</td>
<td headers="row2 col3">8.00</td>
<td headers="row2 col4">108.00</td>
</tr>
<tr>
<th id="row3" headers="col1">In Store Chicago</th>
<td headers="row3 col2">120.00</td>
<td headers="row3 col3">10.00</td>
<td headers="row3 col4">130.00</td>
</tr>
<tr>
<th id="row4" headers="col1">On Web</th>
<td headers="row4 col2">90.00</td>
<td headers="row4 col3">0.00</td>
<td headers="row4 col4">9.00</td>
</tr>
<tr>
<th id="row5" headers="col1">On Web Savings vs. Milwaukee</th>
<td headers="row2 row5 col2">10.00</td>
<td headers="row2 row5 col3">8.00</td>
<td headers="row2 row5 col4">18.00</td>
</tr>
<tr>
<th id="row6" headers="col1">On Web Savings vs. Chicago</th>
<td headers=" row3 row6 col2">30.00</td>
<td headers="row3 row6 col3">10.00</td>
<td headers="row3 row6 col4">40.00</td>
</tr>
</tbody>
</table>
Note that we have dispensed with the scope attribute since it is superfluous in light of this greater granularity. Again, do not use these attributes on a simple table, and try to avoid complex tables altogether if you can.
Explicit Data Cataloguing: the Axis AttributeThe W3C says,
This attribute may be used to place a cell into conceptual categories that can be considered to form axes in an n-dimensional space. User agents may give users access to these categories (e.g., the user may query the user agent for all cells that belong to certain categories, the user agent may present a table in the form of a table of contents, etc."
The headers and id attributes create explicit data associations across cells. The axis attribute, on the other hand, lets you associate a cell with a concept or category that could then be conveyed to the user, perhaps in the form of a table of contents or in a search result.
<tr>
<td axis="location">San Francisco</td>
<td axis="location">Milwaukee</td>
<td axis="location">San Diego</td>
</tr>
<tr>
<td axis="event">Holiday Party</td>
<td axis="event">Resource Meeting</td>
<td axis="event">Resource Meeting</td>
</tr>
A user could set a screen reader to read out the "location" or "event" axes. As of yet, there does not seem to be support for this recommendation.