Notes on Tables and Accessibility

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:

    <th scope="col">Title</th>
    <th scope="col">Director</th>
    <th scope="col">Release Date</th>

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:

    <th scope="row">Star Wars: Episode IV - A New Hope</th>
    <td>George Lucas</td>
    <td>May 25th, 1977</td>
  <!-- ... -->

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" ...>
  <!-- ... -->

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."


Back to blog