Tables Accessibility
HTML tables are often chided for being inaccessible layout methods — they're difficult to interpret by non-visual browsers and more often than not are used against their purpose for presentational effects. Along with the HTML 4.01 standard came some new tags and attributes dedicated to creating accessible tables. It takes a bit of work to make a sophisticated table accessible, but is ultimately worth the effort.
You'll need a good grasp of basic and advanced tables before you take this on.
This page was last updated on 2012-08-21
Browser Compatibility Note:
Some of the tags and attributes discussed here are not fully supported by the current generation of browsers and accessibility devices, but are almost completely backwards compatible and so should be used. » Firefox has no problems, but some browsers, most notably IE6, don't support the row groupings correctly. You should still use them. If a device doesn't understand a tag it can safely skip over it.
Captions and Summaries
The simplest way to make an entire table more accessible is by giving it both a caption and summary. These two elements exist to give readers an overview of the table contents. Therefore, a non-visual browser can read the summary information without having to dredge through the actual table cells to find out what the table is for. The caption
tag, which will appear on the screen is used to give the title of the table, while the summary
attribute gives a broader description of the table's purpose:
<table summary="This table contains a list of items to be bought, along with quantities and prices">
<caption>Shopping List</caption>
<tr><td>...</td></tr>
</table>
The caption
tag must be the first thing after the opening <table>
tag, and there can only be one in each table.
Column Groups (<colgroup> and <col>)
These new, much-needed tags allow you to affect a whole column of your table in one go. In the past, you have been able to give attributes to rows, using tr
, but if you wanted the same attributes down a column you were forced to put them into every td
down the way.
Setting up a <colgroup>
implies a structural grouping among the columns it spans, while the <col>
tag is more of a shortcut to give attributes or stylings to certain columns. Both of these tags have the span
attribute, which allows one tag to control multiple columns at a time.
Some examples are necessary to illustrate these tags in operation. To group the first ten columns in a table together and give them each a width of 30 pixels, drop this code in before any rows or cells:
<colgroup span="10" width="30">
</colgroup>
A colgroup
with no span
attribute has a default span of 1 column. It can take other table attributes like align
and valign
too. If you're going to need a few columns in the middle to be formatted differently, you need to specify them with col
. The following will create a 25-column table with the tenth column being styled differently to the rest.
<table>
<colgroup width="50">
<col span="9">
<col class="special">
<col span="15">
</colgroup>
<thead>...
<tr>...
...</table>
In this example the colgroup
is given a width of 20 pixels. This is inherited by all of the columns spanned by the group. If you're using col
tags, you can't give the colgroup
a span
; instead you must add col
tags until every column has been taken care of. The first col
tag takes care of the first 9 columns, then the tenth column is controlled specifically, and then the rest of the columns are filled in. Note that, even though I'm not applying any further attributes to the last few columns, I still add in this tag so that we've covered every column.
These two tags are also a way to tell your browser how many columns are in your table, by adding up the spans of all of the colgroup
s and col
s. As long as you've specified a main width
for the table
, this allows your browser to display the table incrementally (as it downloads), which is a welcome change from the historical bane of table layouts — they don't display until they've been downloaded in their entirety. For this reason, you need to make sure that you're adding up your columns right.
sourcetip: For all you XHTML-heads, remember that a col
is an empty-element, so needs to be closed with a trailing slash (<col />
)
Row Groups (<thead>, <tbody> and <tfoot>)
In relatively large tables the rows can be grouped into sections, giving the table a bit more form. We can define a header, a footer, and one or more body sections for each table, using the <thead>
, <tfoot>
and <tbody>
tags. What this is hoped to eventually achieve are browsers that can scroll the body of a table independently of a fixed header and footer. Also, if a long table is printed, browsers could repeat header information on each page.
Each of these tags will hold groups of rows inside them. The structure is as shown below:
<table>
<thead>
<tr> ... header rows ... </tr>
</thead>
<tfoot>
<tr> ... footer rows ... </tr>
</tfoot>
<tbody>
<tr> ... first block of data rows ... </tr>
</tbody>
<tbody>
<tr> ... second block of data rows ... </tr>
</tbody>
</table>
Note that both the header and footer information come before any of the table body. This is so that the header and footer can be constructed and displayed before the browser receives the data rows that it will put between them. If your browser does not support this, your footer will appear above the content. Still, browser support is on the way, and Netscape 6 already implements this.
If your table only contains one tbody
section you can leave out the <tbody></tbody>
tags, although I'd imagine they're useful to have in place, for stylesheet implementation and all that jazz. As always in these cases, it's best to leave them in.
Rows and Cells
This is where it gets quite involved. There are numerous attributes for your table cells designed to show the relationship between the cells, most importantly the connections between headers and their data cells. You all know that there are two types of cell — <th>
for header cells and <td>
for data cells. If you've been using header cells to render bold text (I'm sure we've all done it), now is the time to stop. You should choose your header cells carefully, and then connect them to their corresponding data using the attributes below.
Firstly, you can compress the display size of your tables by making use of the cell's abbr
attribute. This can be used in place of the full header text in graphical browsers and to speed up situations where the header text is used repeatedly. For example, when a screen-reader is translating a table to the user it calls out the header's text before each cell. With an abbreviation in place this proves far less irksome and facilitates a quick reading through of the table.
<th abbr="style">User's Preferred Style</th>
Associating Headers
In a graphical browser, it may be obvious which headers refer to which group of data cells. However, if a table is linearised (read cell-by-cell, as is the case when it is read by a screen-reader), the connections are often difficult to see. Cleverly, you can explicitly tell the browser which group of cells the header is linked to with the scope
attribute. This can be set to one of row
, col
, rowgroup
or colgroup
. For example, setting
<th scope="colgroup">Data</th>
means that this header is the header for the whole colgroup
it is contained in. If no explicit colgroup
has been defined, the implicit colgroup
that encompasses the whole table is used in this case.
You can also give the scope
attribute to data cells. Doing so shows that this cell pertains to the rest of it's group, and functions as a header cell. You'll see these 'secondary headings' in the example below.
If you'd like to see a fully-worked example using column groups, heading and scope
, view our accessible table example.
A similar attribute to scope
is headers
, which is applied to data cells to point to their corresponding header cell. First you must give the header a unique id="..."
. Then you simply reference it with
<th id="idvalue">Header Text</th>
<td headers="idvalue">Data</td>
If multiple headers pertain to that one cell you can add multiple comma-separated id
values into it. Adding the header information into every cell can be onerous and unnecessary, as you can also add it to a tr
, col
or colgroup
to reference multiple cells at once.
Finally, you can associate data cells together by categorising them with the axis
attribute. For instance, you could give some cells axis="income"
and others axis="expenses"
. With a good array of axis
values set up a competent browser could call up a chart of categories with all the content of the relevant cells contained within, like a mini-table of contents. Again, this attribute can be set to the broader container tags like colgroup
. No browser currently supports axis
, but it looks like being a useful attribute once it comes into operation.
char / charoff
There is a special method of aligning data, built mainly for tables involving decimalised data, which operates by aligning cell content relative to a certain character. This allows you to line up decimals all at the same centre-point. First you set align="char"
, then char
is used to pick which character you want to use, and charoff
allows you to offset this letter (and therefore the text around it) in percentages of the cell width.
<colgroup align="char" char="." charoff="35%">
Border Styles
The following <table>
attributes modify which parts of its border
are visible. This often helps in visually presenting the relationship of headers to data cells (whether they are vertical or horizontal headers, for example).
The frame
attribute is to be used with border
to decide which parts of the external border will display. The values available are:
void
: displays no borderbox
: displays all four sides (default)border
: also displays all four sidesabove
: displays top border onlybelow
: displays bottom border onlyhsides
: displays top and bottom bordersvsides
: displays left and right borderslhs
: displays left border onlyrhs
: displays right border only
text | text | text |
With these values you can have tables like this:
Which is achieved with code link this:
<table border="3" frame="hsides">
<tr><td>text</td><td>text</td><td>text</td></tr>
</table>
On the inside, rules
is the man to talk to for the internal borders — the lines between the cells. Values:
none
: hides all interior bordersall
: displays all borders (default)cols
: displays borders between columnsrows
: displays borders between rows onlygroups
: displays borders between row groups (thead
,tbody
,tfoot
) and column groups (colgroup
,col
) only.
text | text | text |
These values allow you to create tables like this: