Coding an Accessible Table

We don't use a ton of tables in the archive, but it's worth knowing how to write them well. A table is the right way to mark up three or more pieces of information in a matrix for cross referencing.

Structure

<table summary="Explain what the table does, list the data types it shows, and what you can do with them">
	<caption>Ten words or fewer!</caption>
	<thead>
		<tr>
			<th scope="col">Heading</th>
			<th scope="col">Heading</th>
			<th scope="col">Heading</th>
		</tr>
	</thead>
	<tfoot>
		<tr>
			<th scope="row" colspan="2">Whole table operator</th>
			<td><input type="submit"></td>
		</tr>
	</tfoot>
	<tbody>
		<tr>
			<th scope="row">Row Heading</th>
			<td>Value</td>
			<td>Value</td>
		</tr>
	</tbody>
</table>
				

Some tags in detail

<table summary

It's really useful to summarise what the table does, and what sort of things it displays. Your summary is a "table glance"; it's useful to users of screenreaders, and it's useful for developers in two key ways: (1) so it's not necessary to load a view and put in data to know what it shows, and (2) so you can test the table does what it is meant to do!

An example summary from AO3

Lists all the gifts in the collection, who gave and recieved each one, and what the giver received from the larger exchange.

<caption

Unlike summary, captions are visible on the page. Ours are hidden in our default style, but can be shown with skins.

A caption should be really short and written in simple language. You can copy from the .landmark heading if there is one in the view already. Captions can be very useful to people with cognitive disabilities, and people browsing on small screens and phones, amongst others.

<thead

Group your main headings in a thead. You can have multiple rows <tr> in a thead, so you can further group your headings in more complicated tables.

Never leave the first cell in your thead empty; it should always be a th scoped to the column. Leaving the cell empty causes problems for users of some screen readers.

<th scope="col

It's important to scope your headings, so user agents can tell what heading relates to what part of the table. A scope is a direction. It's obvious when you look at a heading that the bold word above the normal weight word is the heading for that column, but it's not always obvious to a screenreader what heading relates to what. Some user agents announce the relevant headings before every td, which can really help non visual spatial navigation.

<tfoot

It's really weird that the foot comes before the body! It's so that with a really long table, a user agent can print the head and foot to the page and then scroll or paginate the body, and possibly progressively load it.

You don't always need a tfoot, but if you have a "check all" or a final "submit" table row? This is where those things should go.

<tbody

This where the main content of your table lives; it's probably where you're used to a table beginning if you've used tables before, but for us it comes a way down the markup.

<th scope="row

In most of our tables, a row begins with a username, or a work title, or some other kind of object name. It's important to organise tables this way, like with all our markup: first we name something, and then we show it. We mark this up as a row heading as well.

Things to Remember

We don't ever use tables just for layout.

If a table just shows two kinds of data, or "paired" data, it's probably a definition list masquerading as a table and should be rewritten. Most of our user-facing forms are definition lists.

If a table is gigantic and filled with loads of things listed in a row stretching off the screen, like, name, date, tags, summary, user actions, it's a blurb confused about itself and should be rewritten.

If you can't read and understand what a table does in the markup, it's probably confusing and inaccessible to a user. Fix it!

If you want to check your work against some real examples, there are lots of tables in the admin views; a lot of admin work is managing tabular data.


Ten words or fewer!
Heading Heading Heading
Whole table operator
Row Heading Value Value

Going further with tables

Our tables generally show lots of data, and not all text-to-speech agents support scope; we are considering titling our tds in addition to scoping columns and rows, so users always know what property a table value relates to. It's not clear whether this is useful or annoying. At the moment, there are some pilot tables which are both scoped and titled, in tag wrangling, our most used tabular data views. It would be very useful for someone to research this. If you use a screenreader regularly, your input is especially desired.

← Back