Notes on Tables and Accessibility
If you've been in the web development or design world long enough, you might have used a table
element to give your website a multi-column layout. Over the years this pattern became obsolete when the community at large adopted CSS to achieve layout.
One of the many reasons why using a table for layout was decided as poor practice was for the same reason other HTML elements are used properly; semantics. When marking up a website with a table
element for layout, this is an accessibility issue since the content isn't actually tabular data. We want to make sure that what our users are reading and interacting with make sense for the content being presented.
When to use a table?
Sometimes this question may arise when reviewing a design to be implemented:
"How do I markup this content? Is it a table? Maybe it's a list? How do I know?"
Using the appropriate HTML element in order to generate the semantic meaning and context for assistive technology isn't always easy. When it comes to tables, one general rule of thumb to follow is:
"If the content can be aligned within a spreadsheet app and have clearly defined columns and rows, chances are good that using a table
would convey the appropriate context and semantic meaning."
In other words, using a table
isn't bad, as long as it's used to output tabular content.
The scope
attribute
In order to have assistive technology, such as screen readers, make the connection between column/row header cells (th
) and the current cell, the scope
attribute needs to be set in place.
There are two uses of the scope
attribute to consider: on a column header and a row header.
Column header scope
When using a th
element within a thead
section of a table, be sure to apply the scope
attribute. By setting its value to "col" this will announce the column header content alongside the current cell, giving context to what the cell content is referring to.
This would typically be applied to each th
element within a thead
section:
<thead>
<tr>
<th scope="col">Title</th>
<th scope="col">Director</th>
<th scope="col">Release Date</th>
</tr>
</thead>
Row header scope
When using a th
element within a tbody
section of a table, be sure to apply the scope
attribute. Setting its value to "row" will announce the row header content alongside the current cell, giving context to what the cell content is referring to.
This would typically be applied to the first th
cell within the tr
element. Other td
cells would not feature this attribute:
<tbody>
<tr>
<th scope="row">Star Wars: Episode IV - A New Hope</th>
<td>George Lucas</td>
<td>May 25th, 1977</td>
</tr>
<!-- ... -->
</tbody>
Accidentally removing semantics
When applying specific CSS properties to a table
element, it is possible to accidentally remove semantics from the table. Doing so will generate a poor user experience for people using assistive technology to consume and understand the table data; the table itself may not be conveyed as a table
at all.
For example, when removing the default border
styling, browsers will assume, on behalf of your users, that this table is meant to be used for layout, or a "layout table." The built-in heuristics of modern browsers will make this assumption due to the fact that, historically, borders were removed for layout tables.
This is why manually testing your code is critical. Add loading up a screen reader as part of your daily workflow. Conduct some quick tests after completing a major milestone or perhaps even at the end of your workday in order to catch any newly introduced bugs before launching to production.
Tables in email
It's still common practice, even today, to use table layout when creating HTML based emails. And since we know that using a table
element to achieve layout is an accessibility issue, how do we get around this limitation?
Removing semantics
The best method to avoid having an email be incorrectly conveyed as "table" would be to remove its semantic meaning altogether. This can be accomplished by applying the role
attribute to the table
and setting its value to "presentation."
<table role="presentation" ...>
<!-- ... -->
</table>
With this role
value set on the table
, its content will be announced as plain text, as if the table
element wasn't there at all. This helps to avoid the issue of email content being announced as a table
. Of course, all other semantic HTML within the table
will be announced as expected.
When applying the role
attribute, be sure to test your implementation to be sure the correct semantic meaning is being conveyed for screen reader users.
WCAG success criteria
This comes back to 1.3.1 Info and Relationships and 4.1.2 Name, Role, Value which states:
"Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text."
"For all user interface components (including but not limited to: form elements, links and components generated by scripts), the name and role can be programmatically determined; states, properties, and values that can be set by the user can be programmatically set; and notification of changes to these items is available to user agents, including assistive technologies."