It’s Mid-2022 and Browsers (Mostly Safari) Still Break Accessibility via Display Properties

It was late 2020 when I last tested how browsers use CSS display properties to break the semantics of elements.

I had been waiting for Safari to fix how it handles display: contents for four years now, and was excited when the announcement came in June. Then I started testing the latest Safari Technical Preview and the results were not good. That prompted me to give each of Chrome, Firefox, and Safari a fresh run to see how they performed.

The following results are each set in a table with a numbered list following that provides more detail on any cells that are numbered:

Chrome 103 / Windows 11
CSS <tr>, <th>, <td> <ul>, <ol>, <dl> <h#> <button>
display: flex
display: grid
display: block
display: inline-block
display: contents 1 2
  1. <ol>s and <ul>s are not treated as lists, but <dl>s perform fine.
  2. Shows as a button in the accessibility inspector, but does not receive keyboard focus; cannot be navigated with JAWS 2022 button command (B); can be activated with a mouse, touch, and screen reader virtual cursor. 1366037
Firefox 102 / Windows 11
CSS <tr>, <th>, <td> <ul>, <ol>, <dl> <h#> <button>
display: flex 1,2
display: grid 1,2
display: block
display: inline-block 3
display: contents 3 4
  1. <th>s with flex and grid are exposed as cells, not headers. 1711273
  2. Using NVDA 2022.1, the table reports the wrong number of rows and columns and cannot be navigated with table navigation commands (Ctrl + Alt + ).
  3. Inserts text leaf between each list item; NVDA announces each item in the list when navigating by list (L).
  4. Shows as a button in the accessibility inspector, but does not receive keyboard focus; cannot be navigated with NVDA button command (B); can be activated with a mouse, touch, and screen reader virtual cursor; dev tools show a keyboard use warning. 1791648
Safari 15.6–16.1 / macOS 12.4–12.6
CSS <tr>, <th>, <td> <ul>, <ol>, <dl> <h#> <button>
display: flex 1
display: grid 1
display: block 1 2
display: inline-block 1 2
display: contents 1 2 3,4,5,6
  1. VoiceOver announces the wrong column and row count for each table; VoiceOver does not announce column headers when moving between columns; VoiceOver table navigation commands do not work (Ctrl + Option + + ). 239478, 239479
  2. VoiceOver does not announce ordered or unordered lists as lists (description lists are fine); VoiceOver does not navigate to ordered or unordered lists with list navigation (Ctrl + Option + + X). 243486, 243373
  3. Does not receive keyboard focus; cannot be navigated with VoiceOver button command (Ctrl + Option + + J); can be activated with a mouse. 243486
  4. As of Safari TP 151, can be navigated with VoiceOver button command (Ctrl + Option + + J) but its accessible name is not announced.
  5. As of Safari TP 152, its accessible name is announced.
  6. Safari 16 has regressed from Safari TP 152; the accessible name is not announced and it has none in the accessibility tree.
Chrome 103 / Android 12
CSS <tr>, <th>, <td> <ul>, <ol>, <dl> <h#> <button>
display: flex
display: grid
display: block
display: inline-block
display: contents 1
  1. <ol>s and <ul>s are not treated as lists, but <dl>s perform fine.
Safari / iPadOS 15.5
CSS <tr>, <th>, <td> <ul>, <ol>, <dl> <h#> <button>
display: flex
display: grid
display: block 1 2
display: inline-block 1 2
display: contents 1 2 3
  1. VoiceOver treats all cells as in column 1. 243474
  2. VoiceOver does not announce ordered or unordered lists as lists (description lists are fine).
  3. Fires on double-tap with VoiceOver.

Verdict

Chrome cleaned itself up and has held steady for more than 20 versions. Firefox needs work, but at least saw a tiny improvement. Safari made one tiny improvement but has a much bigger deficit, and I don’t trust it will improve much given its now twice-promised fix for display: contents.

Tests

The following four embedded CodePens are what I used for testing. These do not account for all scenarios or use cases, but at least provide a baseline set of results. They were all created in early 2020. I adapted them by adding horizontal rules between each test since VoiceOver was concatenating some elements with display: contents to the prior element.

Each links to a CodePen debug mode so you can test it directly, without the iframe or wrapper cruft.

HTML Tables

Visit the tables test in debug mode.

See the Pen Tables with Assorted Display Properties by Adrian Roselli (@aardrian) on CodePen.

Lists

Visit the lists test in debug mode.

See the Pen Lists with Assorted Display Properties by Adrian Roselli (@aardrian) on CodePen.

Headings

Visit the headings test in debug mode.

See the Pen Headings with Assorted Display Properties by Adrian Roselli (@aardrian) on CodePen.

Buttons

Visit the buttons test in debug mode.

See the Pen Buttons with Assorted Display Properties by Adrian Roselli (@aardrian) on CodePen.

References

I have not gone through all the old browser bugs, identified outstanding open bugs, nor filed fresh bugs. I just don’t have the energy. I am hoping readers can confirm or deny my findings, however, so if/when I muster that energy I have confirmation these issues aren’t just a failing on my end.

I have written about display properties, tables, and accessibility a bunch over the last few years. Others have also kicked in a pile of posts. This list is not exhaustive.

Update: 15 July 2022

I heard back from someone who works on WebKit (finally) and he confirmed the issues I found with the following bugs:

Update: 22 July 2022

I tested Safari Technology Preview 149 today. No change.

Update: 4 August 2022

I tested Safari Technology Preview 150 today on macOS 12.5. No change.

Update: 6 August 2022

More updates from Tyler Wilcock, namely that three commits have landed in WebKit that address the heading and button examples in this post:

I am thrilled to see the now-rapid progress from Tyler specifically. Compared to the current silence from Apple’s dedicated developer relations arm, past Apple mis-representation of fixes, and years of Apple’s failure to test for these obvious issues, this has been a breath of fresh air.

No idea which Safari Technical Preview will see these updates, but I am hopeful they will make it to Safari 16.

Update: 16 August 2022

I tested Safari Technology Preview 151 today on macOS 12.5.1. No change except a button with display: contents is now partially exposed in one context.

Update: 27 August 2022

I tested Safari Technology Preview 152 today. No change except a button with display: contents now has its accessible name announced.

Update: 11 September 2022

I tested Safari Technology Preview 153 today. No changes.

Update: 12 September 2022

I tested Safari 16 on macOS 12.6 today. It is equivalent to Safari TP 151. Tables with display properties are difficult to impossible to navigate with VoiceOver. Buttons with display: contents have no accessible name and do not activate on Enter. The minor improvements since Safari 15 (and before) are not enough to make buttons or tables accessibility supported on macOS. All this despite the following claim today:

Safari 16 introduces a re-architecture of WebKit’s accessibility support on macOS. […] Safari 16 also greatly improves accessibility support for elements with display:contents by ensuring they are properly represented in the accessibility tree.

And this claim in June 2022:

And this claim in March 2022:

WebKit fixed an accessibility bug with display:contents, which incorrectly hid content from the accessibility tree. This fix makes it possible to freely use this display value to remove a box from the DOM tree, while still including its children. This can be useful when you want to adjust which box is the child of a Flexbox or Grid container, for instance.

I really wish Apple had a dedicated Safari Twitter account instead of hanging its evangelist and/or the open source WebKit project out there to take the heat. That is unfair.

Remember that this is not an excuse to beat up the devs at Apple. If you are doing that, you are being unnecessarily mean to individuals who may have no control over broader organizational decisions. Do not be that guy / person / jerk.

Update: 13 September 2022

I filed an issue to update the Can I Use entry for display: block, and it was merged overnight: #6451: Safari 16 (release version) still broken

The Can I Use flex and grid data comes from the MDN browser-compat-data, so I filed an issue there: #17776 css.properties.display – Safari (thru 16) flex, grid, contents problems

Update: 17 September 2022

Apple’s devrel team came through and filed another PR at Can I Use (while still engaging me in my PR) and undid my changes. The good news is that for the first time Can I Use reflects that Safari 15.x and older never properly supported display: contents, acknowldegment of its prior false claims. The less good news is that both Apple and Can I Use believe that Safari supports display: contents when using it on buttons or tables renders them inaccessible in Safari 16. Clearly we have different ideas of the term supported.

Update: 20 September 2022

Following some informal community polling, Can I Use has restored Safari’s “partial support” entry for display: contents:

The Can I Use table for display contents showing Safari 16, 16.1, and TP all only partially support the feature.
CSS display: contents on Can I Use

My efforts have probably earned me no fans at Apple, though at least one Apple devrel person might agree with the change.

Update: 13 November 2022

No improvements in Safari 16.1. Here I demonstrate how the row and column counts are way off for the tables.

Pressing Ctrl + Option + + T is a quick way to move down the tables and confirm the counts.

Here I am using Chrome 107, Firefox 106, and Safari 16.1 trying to Tab to the <button> with display: contents. It is worth noting that neither adding role="button" nor tabindex="0" will fix this.

Pressing Tab will not get you to a <button> with display: contents in any browser.

Update: 29 November 2022

Promising work on resolving a Firefox bug where <th>s with CSS flex and grid are exposed as cells, not headers:

This revision fixes the problem by moving the th NativeRole logic into the TableCellAccessible class, then calling that logic from both the ARIA grid cell accessible NativeRole and from HTMLTableHeaderCellAccessible, as before. This revision also updates tests reliant on the old behavior, including beefing up an existing test aimed at this bug specifically.

When that deploys it will leave Safari ≤16.1 as the last browser that breaks tables using CSS display properties.

Update: 20 December 2022

Stumbled across this piece of advice on Twitter promoting the use of display: contents:

The HTML <details> element1 cannot become a flexbox or grid container (display: flex and display: grid are ignored). This restriction is specified in the HTML Standard (The <details> element is expected to render as a block box2) and implemented in all browsers. It is possible to work around this limitation with display: contents3. […]

  1. developer.mozilla.org/docs/Web/HTML/Element/details
  2. html.spec.whatwg.org/multipage/rendering.html#the-details-and-summary-elements
  3. mastodon.social/@simevidas/109538578812286095

[…]

This is a case of trying to solve one very specific problem, layout core to an element, with a flamethrower and no regard for what else goes up in flames. In this case it is keyboard accessibility. To demonstrate that, I made a demo:

See the Pen Untitled by Adrian Roselli (@aardrian) on CodePen.

This is the outcome of specs that do not clarify how the content model should (or should not) be affected, browsers that ignore the bug for years, and developers who cannot be bothered to test with the one device attached to all their computers (keyboard, for those reading who never use on for testing).

The CSS display: contents property looks like such a simple salve, but in the end guarantees an access barrier.

4 Comments

Reply

I encounter similar issues when I wish to set grid on the body element and display contents on the main element. The main issue I have is implementing a skip to content link that targets the main[display: contents] element :(
I think this should be of interest to the community since the following is a common DOM pattern:

Skipping the main element would really be a shame

Neil Osman; . Permalink
In response to Neil Osman. Reply

part of my comment was sanitized, so here is a demo that illustrates my point
https://codepen.io/WW3/pen/XWEVoqN

Reply

Thank you for posting this and for continuously testing for the current state of things. display: contents seems really valuable as CSS grid becomes more commonplace, but until some of these issues are fixed I’m still going to avoid grid entirely (where feasilbe) in cases where display: contents would be necessary.

Clayton; . Permalink
In response to Clayton. Reply

FWIW, I think an alternate approach might be to test the problematic instances on your audience’s browsers and then decide which cases you can ignore and which cases are problems. But I very much understand the frustration — I have been there for a few years now.

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>