Consider this post to be the sequel to my 2012 post It’s OK to Use Tables. Here I will go into bit more detail based on the state of accessible efforts I see today.
In that post I identified two scenarios I see frequently as a result of developers blindly following the don’t use tables mantra of near-modern web development:
Tables displayed via images, usually with useless alternative text,
Tables assembled from the flammable tinder of <div>s.
Here we are in 2017 and the slow oozing of ARIA into frameworks and libraries has emboldened the anything-but-a-table movement — because developers feel they can re-create any table semantics they excise.
Couple this with some confusion about how to make a standard table accessible, and we have a bit of a mess. Enough of a mess that nearly every accessibility audit I perform has a non-<table> table in it.
I want to qualify that I am talking about data tables, not layout tables. Data tables have two axes of information. If you only have one axis of information, use a list.
I am going to give you enough information, hopefully, to make accessible, responsive tables on your own that are not a burden on you nor your users.
This part is really easy. Just make a valid HTML <table>. You don’t even need that many elements. Avoid spanning cells, make sure you use <th> for headers, and adding a <caption> is nice of you. I made a CodePen of a simple table, and embedded it below.
You may find it hard to believe, but for a straightforward data table, you probably do not need any ARIA. Screen readers in particular have been dealing with tables for far longer than the existence of ARIA.
Sometimes a table is just a grid of data and sometimes a table is a control (widget) that users modify (or modify its data, such as a spreadsheet). The control/widget use case produced some ARIA to account for it. This ARIA is what is so frequently abused when developers over-code to avoid using a <table>.
ARIA has a few roles that define and describe data grids. The definitions that follow come from ARIA 1.1 (these are truncated).
A composite widget containing a collection of one or more rows with one or more cells where some or all cells in the grid are focusable by using methods of two-dimensional navigation, such as directional arrow keys.
A structure containing one or more row elements in a tabular container.
Please note that those roles are primarily intended to be used for interactive grids, such as a spreadsheet. In these controls each cell may be editable and can be navigated using a series of keyboard shortcuts.
Since developers were applying grid roles to what should be regular tabular data, ARIA 1.1 added the following role in response:
[ARIA 1.1] A section containing data arranged in rows and columns. See related grid.
The table role is intended for tabular containers which are not interactive. If the tabular container maintains a selection state, provides its own two-dimensional navigation, or allows the user to rearrange or otherwise manipulate its contents or the display thereof, authors SHOULD use grid or treegrid instead.
Authors SHOULD prefer the use of the host language’s semantics for table whenever possible, such as the HTML table element.
If it’s not a widget, don’t use role="grid", use role="table". Ideally you do this by just using <table>, because then you can skip the role="table" as <table> has that role implied.
Putting all the ARIA roles into practice and understanding which to use can be tricky. The WAI-ARIA Authoring Practices 1.1 document is intended to be a guide for developers, providing copy-paste-ready code for common user interface patterns.
The ARIA Authoring Practices document defines a pattern for grids, providing guidance and sample code. This sample code includes the nesting requirements and the ever-complex necessary keyboard navigation support covering the following: ↓, ↑, →, ←, Home, End, Page Down, Page Up, Ctrl + Home and Ctrl + End.
There are even more keys to support if you allow selection of cells, rows, or columns.
In short, if you start using role="grid" on tabular data then you are telling users that it is a fully interactive widget. You also need to implement all the keyboard support above and more.
There is a placeholder in the ARIA Authoring Practices document for tables, though it points back to the grid pattern and the following decision flow for choosing between a table and a grid:
A grid is a composite widget so it:
Always contains multiple focusable elements.
Has only one focusable element in the page tab sequence.
For a table, all focusable elements contained in a table are included in the page tab sequence.
To restate: a grid is a single tab stop on the page (which then manages focus within), while a table is not.
With a little creativity, you don’t need to worry about narrow viewports. Granted, not all layouts will work in all cases, but there is no reason to dump <table>s in service of responsive layouts. Here are a couple methods you can use.
To reiterate, I am just talking about width responsiveness. I have another post coming that goes into more detail.
Just Let It Scroll
Yep, you can just let a table scroll off the screen (within a bounded container). It’s not ideal and can be offensive to some, but it is at least simple and straightforward. The catch is that you need to make the scrolling area keyboard accessible, but we already know a solution for that.
I made a sample table in a scrolling container. I added tabindex="0" so keyboard users can get to the scrolling area and use the arrow keys to scroll back and forth. I added role="region" and aria-labeledby so that screen reader users who encounter this tab stop will understand what it is. I have embedded it below or you can go directly to it at CodePen.
For some tables you can just use CSS to re-arrange the entire thing. Just because table elements come with built-in table layout styles doesn’t mean you are restricted from changing them.
Between CSS flex and CSS grid, we have more options than ever to make responsive tables that can adapt to nearly any screen regardless of your data.
Note that as soon as you start messing with the display properties of a <table> (using the display property), it no longer registers as a table to a screen reader. I will go into more detail on that in my next post.
I have made an example that uses neither CSS flex nor CSS grid, for backward compatibility reasons. Instead it relies on the lowly CSS display: block. Obviously in my example it won’t work for anything more complex than simple values, but it at least shows you what is possible. You may view it on CodePen or just play with the embed below.
Herin, the answer to that depends on what you are doing. Some thoughts:
Links in a table are generally fine, unless every cell is a link, then it can get verbose for screen readers.
A table that looks and functions like Excel is best suited with the ARIA grid patterns.
If you have a table with one column that has a checkbox or radio button, that in itself is fine provided you give each one a unique and descriptive accessible name (which may be via aria-label or aria-labelledby.
Sortable columns will also need to be controls that themselves are accessible and live in the column header.
In short, make sure any controls you add to the table are accessible, that they do not make the table too verbose, and do not turn your table into Excel.