Tables, CSS Display Properties, and ARIA

February 20, 2018 ; 17 Comments

This post has two separate but related things going on. One is an example of one of my responsive tables with ARIA added, and the other is the Twitter conversation that started this along with some generalized responses.

Responsive Table with Semantics Retained by ARIA

See the Pen Responsive Table with Semantics Retained by ARIA by Adrian Roselli (@aardrian) on CodePen.

This example (viewable directly at CodePen) shows how you can use ARIA table roles to override / re-insert the table semantics you may lose by using CSS flex or grid.

First, you need to understand how to use the roles, including getting the nesting right. In general these can be simple. The table role should be added to the <table> element. If you use <thead> , <tbody> , and/or <tfoot> , they get the rowgroup role. <tr> gets row and <td> gets cell . <th scope="col"> gets columnheader while <th scope="row"> gets rowheader .

It really is that straightforward.

But here is where it gets complicated. Tables that hide content (especially headers) in a responsive or similar context, and tables that re-order content.

The re-ordering issue is easy to solve — don’t use CSS to re-order table content. Use client-side script to move the nodes around in the DOM. For example, using CSS flex or grid to sort a table may be novel, but it will not support screen reader users.

Hiding content is a bit trickier. My example above addresses that. It uses the same code as my responsive accessible table, but with two differences. It now has the ARIA roles I outlined above, and it does not add the replacement text for the row headers until you click the button. The button is only there to give you a chance to experience the table without the replacement text (in a narrow view).

Examples

The following videos demonstrate how the ARIA can make the table semantics, and thus table navigation, work within the browser. The first one demonstrates how the hidden column headers hurt our ability to understand the narrow table (not just visually). The second video demonstrates how the table is more understandable once the generated content appears to take the place of the hidden column headers.

Each video uses the embedded CodePen example in Firefox 58.0.2 with NVDA 2017.4 on Windows 10. I am using table navigation ( Ctrl + Alt + → / ← / ↑ / ↓ ) to get around the content, jumping up and down rows and between cells.

Advice

If you are not able to test with a screen reader, maybe don’t do this. You run the risk of making an already problematic responsive table downright unusable. Further, if you are not going to test regularly as browsers and screen readers get updated, maybe don’t do this.

The Tweet

I pushed out a tweet yesterday that generated a lot of interesting questions and conversations. I think it may have also led to some confusion.

Using CSS flex on a <table> overrides its native semantics, rendering it useless to a screen reader. Avoid.adrianroselli.com/2015/10/html-s… Adrian Roselli (@aardrian) February 19, 2018

I followed it up to add some detail:

I should add, using display: block | grid | inline | … on table elements will also override native semantics. Not unique to flex. Adrian Roselli (@aardrian) February 19, 2018

Collected Responses

I received some interesting questions and comments, some of them tinged with frustration. The most critical thing I learned is that developers who play around with CSS and tables on the whole do not test them in screen readers. This is my effort to break down the types of questions and comments and provide some extra information.

Developers have been breaking HTML table semantics via display properties since before CSS flex and grid.

This should not make it harder / impossible to make responsive tables. I wrote some easy and less easy ways to make responsive tables.

CSS already has as impact on HTML semantics — display: none is an example of that.

is an example of that. If screen readers give priority to mark-up over styling in every instance, then display: none would no longer work.

would no longer work. Setting display: table and related does not impart HTML table semantics to ( <div> ) layouts that used it for things like vertical centering.

and related does not impart HTML table semantics to ( ) layouts that used it for things like vertical centering. This is not a function of screen readers alone, as they get their accessibility information from the browser.

A screen reader needs more than the DOM to understand a page, so asking it to ignore all but the DOM is impractical.

Users don’t want us to be able to detect screen readers, nor should we and certainly not as a workaround for responsive tables.

Even if you could detect screen readers, how would you know which third of those users are not blind?

Even if you listen for mouse actions as a proxy for sighted screen reader users, how do you account for those with mobility impairments who do not use a mouse? Or mobile screen reader users who rely on touch gestures?

Disabling a site’s CSS for screen reader users is therefore impractical (and a terrible, terrible idea).

Using flex or grid for an HTML table already means you are running the risk of messing up content order versus source order (not unique to tables).

Using CSS grid to lay out an HTML table may be fine, but it still won’t be a table semantically

A significant part of why we have this situation is because of years of relying on HTML tables for layout that had their display properties tweaked, providing a cue to screen readers that they were for layout only.

Screen readers already have their own heuristics for dealing with tables, primarily due to years of poor developer practices, with current behaviors tracked by PowerMapper.

While ARIA provides roles for tables, it requires you to understand your tables well and stay on top of screen reader testing as new releases come.

Applying ARIA roles to retain table semantics can be problematic for responsive tables where header cells or other cells are discarded, a common pattern in responsive table examples. See the previous section of this post.

Generally, do not use ARIA to try to override CSS, not just because the CSS may not load, but because this is not the purpose of ARIA.

If you find the ability to so casually dismiss HTML table semantics to be frustrating, it is not the fault of screen readers. It is the fault of years of terrible coding practices predicated on visual layout over developer rigor of choosing the right element for the job.

If you find you want to use CSS flex, grid, block, inline, or other display properties on a table, then maybe consider what the heck you are doing with the table.

Assorted CSS Specifications

I may have stayed up too late trying to parse all this.

CSS 2.x tries to break down the role of CSS for tables, namely leaving it up to HTML to define the semantics:

Table layout can be used to represent tabular relationships between data. Authors specify these relationships in the document language and can specify their presentation using CSS 2.1. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification, W3C Recommendation 07 June 2011, edited in place 12 April 2016: 17.1 Introduction to tables

CSS 2.x also accounts for non-HTML pages, where it adds the semantics and generates the necessary anonymous table objects around itself:

In other document languages (such as XML applications), there may not be pre-defined table elements. Therefore, CSS 2.2 allows authors to “map” document language elements to table elements via the ‘display’ property. For example, the following rule makes the FOO element act like an HTML TABLE element and the BAR element act like a CAPTION element: Cascading Style Sheets Level 2 Revision 2 (CSS 2.2) Specification, W3C First Public Working Draft 12 April 2016: 17.1 Introduction to tables

I can find nowhere in the CSS 2.x specification (specifically in the visual formatting model) where the display property should override the native semantics of the source document language (HTML).

The closest statement about interaction with tables that I can find in the Flexbox specification is a note directed at user agent makers, and says nothing about changing the semantics:

Note: Some values of display normally trigger the creation of anonymous boxes around the original box. If such a box is a flex item, it is blockified first, and so anonymous box creation will not happen. For example, two contiguous flex items with display: table-cell will become two separate display: block flex items, instead of being wrapped into a single anonymous table. CSS Flexible Box Layout Module Level 1, W3C Candidate Recommendation, 19 October 2017: 4. Flex Items

CSS Grid has similar language:

Note: Some values of display normally trigger the creation of anonymous boxes around the original box. If such a box is a grid item, it is blockified first, and so anonymous box creation will not happen. For example, two contiguous grid items with display: table-cell will become two separate display: block grid items, instead of being wrapped into a single anonymous table. CSS Grid Layout Module Level 1, W3C Candidate Recommendation, 14 December 2017: 6.1. Grid Item Display

In short, I can see nowhere in the CSS, grid, nor flex specifications where a user agent should override the native semantics of the source document language (HTML). Based on that, my read is that browsers dumping table semantics is a bug.

Example

I made videos with a screen reader to try to demonstrate how you can ruin the ability to navigate and parse a table just by adding flex.

The table from the example with CSS disabled as heard in NVDA. I am using table navigation controls. NVDA announces the number of rows & columns, all the headings when hopping cells, and tells you when you hit the edge of the table.

The same table with CSS flex added, as heard in NVDA. It is longer presented as a table. The tab order when tabbing through the links is confusing, headers are not announced, sorting controls do not work.

What You Can Do

Help purge the web of layout tables. Help purge the web of CSS table display properties just for vertical centering. Code tables properly and accessibly.

In conjunction with that, file issues against web browsers. Browsers parse the page and then hand it off to a screen reader. If you can get the browsers to behave consistently, then you can get screen readers to adapt as well, as they may no longer need to rely on heuristics to protect users from bad code.

In the meantime, test your responsive tables with screen readers.

Maybe weigh in on the discussion [Proposal] new attribute for specifying focus and reading order at WICG Discourse.

Roger Johansson wrote a piece in 2011 that wraps up both how browsers destroy semantics on tables and lists when CSS display properties are applied: Screen readers and CSS

This quote from Steve Faulkner’s post Short note on what CSS display properties do to table semantics nails it (it being who is to blame for this mess):

[I]t’s either wittingly/unwittingly the fault of the developer or the browser. But what we can be sure of, in these cases, is that it is not the fault of the screen reader. Short note on what CSS display properties do to table semantics

It is worth noting that display: contents on a <table> , <ul> , <ol> , etc. also hides it from screen readers. Granted, it’s late and I only tested in Firefox and NVDA so far, but you can try it out yourself (embedded below or visit it directly at CodePen):

See the Pen Table with display:contents by Adrian Roselli (@aardrian) on CodePen.

Read Ire Aderinokun’s post, How display: contents; Works for other ways display: contents can affect content on a page.

Big progress. Chrome 80 no longer drops semantics for HTML tables when the display properties flex , grid , inline-block , or contents are used. The new Edge (ChromiEdge) follows suit. Firefox still dumps table semantics for only display: contents . Safari dumps table semantics for everything.

Chrome v80 no longer dumps table semantics when CSS flex is applied.



Test: cdpn.io/aardrian/debug/xxGEKKJ



Not only display property impact parity with Firefox, but passes Firefox (since display: contents still eats table semantics in Firefox but no longer in Chrome). pic.twitter.com/4iuitwgWhS Adrian Roselli (@aardrian) February 19, 2020