Tables, CSS Display Properties, and ARIA

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.

I followed it up to add some detail:

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.

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.

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.

Update: March 3, 2018

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

Update: March 4, 2018

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.

Update: March 27, 2018

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.

Update: February 19, 2020

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.

Update: September 29, 2020

Léonie Watson has just posted How screen readers navigate data tables where she walks through a sample table to get some information, explaining each step, the keyboard commands, and the output. She also links to a video demonstration, which I have embedded below.

Watch How screen readers navigate data tables at YouTube.

Update: December 7, 2022

I should have linked to my updated post sooner: It’s Mid-2022 and Browsers (Mostly Safari) Still Break Accessibility via Display Properties

Chrome is fine now, the last Firefox bug just closed and should deploy soon, and Safari is still a hot mess after repeated assurances it was being fixed.

20 Comments

Reply

The videos would be a better demonstration if anyone not used to a screenreader would understand them. I doubt that there are many, not used to screenreaders, could understand anything in that speed. Could you provide thos videos in a slower speed? The actual speed must be 6x or 8x speed :-)

In response to Jens Grochtdreis. Reply

Jens, I am on a deadline, so I won’t be re-making these videos in the short term. In the meantime, I suggest viewing this page in Edge or Firefox, which allows you to watch a video at half speed. I wrote some details on this feature in a post.

Reply

Hi Adrian, thanks for the suggestion. Would have been nice, but the quality of the half-speed video is worse. I cannot understand anything. Well it was an idea. Ignore my plea from the first comment. Nevertheless an interesting finding although anyone not used to a screen reader won’t understand anything.

I keep the fingers crossed for your deadline :-)

Reply

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

Help purge the web of CSS table display properties just for vertical centering.

Aren’t these statements kind of contradicting each other? If using table display on non-table elements is not meant to give them table semantics (and web devs have been taught for years that it doesn’t, and consciously use it for visual-only layout tweaks!) — why should we stop using it just because some software erroneously treats such elements as real tables, contrary to the common developers expectations (according to the data collected in the corresponding Firefox bug, currently fixed)? The section 4.9.1 of both HTML specs also has a note directly suggesting using CSS table model as one of the alternatives to (ab)using HTML tables for layout. Why should the browser/AT incapability to distinguish the semantically wrong HTML layout and the semantically correct DOM with some visual-only tweaks for minor layout enhancement be the problem of web devs who consciously use HTML to express semantics and CSS just to present it nicely in certain circumstances, not of the developers of these browsers/AT who should improve their heuristics to match the web devs intentions better?

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.

Unfortunately, not all design requirements can be currently reasonably met with CSS table model. The simplest example from my recent experience is fitting the whole table into the container width (wrapping the sell text if necessary) while having one column shrink-wrap to its longest content. Table-layout: fixed can do the former, table-layout: auto can do the latter, but neither can do both. Grid layout (accompanied with display: contents for table rows) would solve it.

I agree that limiting ATs to DOM only is impractical and the problem is more complicated than one might think at first glance. But just prohibiting web devs to use certain CSS features seems not to be a long-term solution to me. I guess we should gather stats of how these features are really used and what web devs are meaning by them, collect the feedback of actual ATs users which behavior is helpful for them and which is not, and file bugs to browsers and ATs if necessary — as it seemed to work in Firefox case above…

In response to SelenIT. Reply

Aren’t these statements kind of contradicting each other?

No. The first statement describes where we are today. The second statement is a plea to hopefully help make it easier for heuristics to make better guesses (by not abusing the intent of the CSS display property).

Related, there is a Firefox bug that changing the display property of a <table> blows away its semantics. It is still open. The bug you cited (and which was resolved) was about the addition of table semantics to non-<table> elements just because display: table is added. They both need to be addressed to prevent the issues we are experiencing.

You ask why this should be the problem of web developers, and I answer that it is our job. It has always been our job. We are supposed to make things work for our users and file bugs when the specs and browsers get it wrong.

Let me try a heavy-handed analogy — it should be up to municipalities to fix potholes on their roads so car makers do not have to install shocks. So let’s build cars with no shocks. That will force municipalities to fix their roads.

Unfortunately, not all design requirements can be currently reasonably met with CSS table model

I have never needed to use CSS table layout in a project, after building easily hundreds of sites and applications. As a result, I struggle to understand the limitations of CSS table layout since it was never part of my process. My suggestion above is an effort to simplify the heuristics that browsers use to decide what is and isn’t a data table.

I am also suggesting not overriding real HTML tables with CSS display properties, both to developers and browsers.

In response to Adrian Roselli. Reply

Adrian, thanks for your reply!

But I’m still a bit confused about the first statement: doesn’t it turn out that display:table/table-cell does affect the semantics of the ‹div› element (at least in Chrome)? Or do you suggest that changing the semantics of the element by just changing its styling is OK? If so, than, using your cars/roads analogy, this sounds to me like “the potholes are OK, people who complain about them are only trying to save on shocks in the cars they build”. For me, it’s kind of false dichotomy: what car users really want is probably to drive in the cars with good shocks on the roads with no potholes:)

The Firefox issue about tables loosing their semantics because of the `display` value is filed as a bug — yet open, but still a bug, not a feature, not “resolved wontfix”. I agree that it may be hard to decouple the semantics from the visual layout (they are called “screen readers”, after all!). But… being unable to use a proper markup to express the intended meaning and visually enhance it with advanced layout tools because browsers can’t treat it as intended because their heuristics are optimized for the old messy markup feels kind of unjust. It’s like people who consciously follow the HTML spec recommendation get punished for the sins of the ancient web coders who had no idea what they were doing.

I agree that currently we should avoid overriding the native display, as it’s really problematic, and by no means call for “building cars with no shocks”:). But I don’t want this situation to last forever. I hope this second Firefox bug will be resolved at some point, as well as similar bugs for other browsers (if they are not filed yet, they should be). Screen readers shouldn’t drop the table semantics from something that is marked up properly as a data table, visually looks like a nice data table and quacks like a data table, just because it doesn’t use the “magic” CSS value for its display. The less such implicit “magic” would be there in the platform at all, the simpler would be the browser heuristics, and the easier it would be for web devs to create the best experience for all the users. Of course, we need some stats first, how much existing content relies on that “magic” — but I hope it’s not really that much of it.

I also couldn’t find any place in CSS specs saying anything about influence of CSS display to the meaning. The only place where CSS is mentioned as a possible indicator of table meaning seems to be the HTML spec saying that “Explicit visible borders set using CSS” mean that a table is “Probably a non-layout table”. But the introduction section in the CSS Display spec draft has a note mentioning that display can interact with the speech output, and links to the CSS Speech module (though very outdated). Maybe the CSS Display module is the best place to clarify this issue as well?

In response to SelenIT. Reply

doesn’t it turn out that display:table/table-cell does affect the semantics of the <div> element (at least in Chrome)?

No. Check the Chrome accessibility inspector. It is just a generic container.

Or do you suggest that changing the semantics of the element by just changing its styling is OK?

No. Since much of the rest of your response is predicated on one or both of those being yes, I’ll just tackle specific points you make.

It’s like people who consciously follow the HTML spec recommendation get punished for the sins of the ancient web coders who had no idea what they were doing.

Much of HTML 5 was built to reflect current and historic practice. If anything, the spec is the result of those earlier coders.

I hope this second Firefox bug will be resolved at some point, as well as similar bugs for other browsers (if they are not filed yet, they should be).

You can contribute by looking for open issues and posting your concerns, and when those issues do not exist, filing them.

Maybe the CSS Display module is the best place to clarify this issue as well?

I see some new bug filings in your future!

Reply

Tables are probably toughest to make responsive. Most approaches we tried were based on div / span – however it has its own trade off.

I like this approach better. Though, some issues I see are minor.
Can we not assume role=’row’ for any tr ? I am not feeling comfortable with the redundancy of calling it tr and role both where it can be inferred.

In response to Dev. Reply

You can experiment with removing the role from <tr> and testing it with more screen reader / browser combinations. I agree, I do not like having to add them either.

In response to Adrian Roselli. Reply

Toughest part for me right now is to debug these stylings and JS issues in iOS devices. For most part safari does help, however when it does not work – it does not work and frustrates me to hell.

May be I have not come across a better tool to debug CSS / JS in actual devices. Browser addons have kind of spoiled my creativity I guess.

I would like to know how do u debug these one of device specific issues that wont happen on a simulator.

In response to Dev. Reply

Just to give more context on a sample issue I am currently struggling. It is actually JS issue but cant debug it –

I wrote this simple math practice tool for my kid to learn math tables.

obviously it had to be responsive so I picked a template that worked pretty well for styling part. But my JS seems to not work on iPad

it works on safari with ipad user agent.

http://math.imustread.com/multiplication/practice.html

this is open source on github here
https://github.com/fromdev/math/blob/master/multiplication/practice.html

In response to Dev. Reply

Short of getting your hands on a device and running a remote debugger, there isn’t much help I can offer on testing on your iPad. A Google search brought me to this set of instructions for remote debugging. It is from 2014 but updated a few days ago.

Reply

Hi Adrian,

It’s Teddy again. Just noticed that Voiceover on my iPhone isn’t recognizing the ARIA table as a data table and doesn’t read the caption, but it does read the rest of the table’s contents.

Whereas it was the opposite with your previous no-ARIA semantic table, recognized the table, read the caption, but couldn’t read the rest of the table.

Teddy; . Permalink
Reply

Update: This is marked as fixed in Firefox 62. Just checked Steve’s test case in the accessibility tree and in NVDA and it looks fixed indeed, for display:block.

For Flex and Grid the table is identified as such, but table nav is broken.

In Chrome + NVDA all the non-default tables are just skipped.

Reply

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

For Safari + VoiceOver on iOS specifically, this does incorrectly announce table semantics in the form of, “Table start…” and “Table end…”

Demo on CodePen.

Recommended to use Flexbox instead for layout.

In response to Scott. Reply

Thanks, Scott. The note and demo are helpful.

Reply

[…] Tables, CSS Display Properties, and ARIA | Adrian Roselli – More on responsive, accessible tables with ARIA […]

Reply

Thanks for the good article. Do we know if FF or Safari have stopped dumping table semantics when CSS display is applied?

Robert; . Permalink
In response to Robert. Reply

Firefox 94 seems fine. My Mac is [waves arm] over there, so I cannot check it right now. However, if you take the embedded pen from above and inspect the accessibility properties you can confirm support (or lack). As for when this was fixed, you can dig into the issue history for specific release. Either way, things are much better now then they were 3½ years ago.

Reply

Just found the article. It was very helpful. Big thank for updating it.

Anon; . Permalink

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>