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.
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 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 with no
span attribute has a default span of 1 column. It can take other table attributes like
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.
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
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
cols. 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 (
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
<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:
<tr> ... header rows ... </tr>
<tr> ... footer rows ... </tr>
<tr> ... first block of data rows ... </tr>
<tr> ... second block of data rows ... </tr>
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>
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
colgroup. For example, setting
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
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>
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
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
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%">
<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).
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 border
box: displays all four sides (default)
border: also displays all four sides
above: displays top border only
below: displays bottom border only
hsides: displays top and bottom borders
vsides: displays left and right borders
lhs: displays left border only
rhs: displays right border only
With these values you can have tables like this:
Which is achieved with code link this:
<table border="3" frame="hsides">
On the inside,
rules is the man to talk to for the internal borders — the lines between the cells. Values:
none: hides all interior borders
all: displays all borders (default)
cols: displays borders between columns
rows: displays borders between rows only
groups: displays borders between row groups (
tfoot) and column groups (
These values allow you to create tables like this: