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:
CSS | <tr> , <th> , <td> |
<ul> , <ol> , <dl> |
<h#> |
<button> |
---|---|---|---|---|
display: flex |
✔ | ✔ | ✔ | ✔ |
display: grid |
✔ | ✔ | ✔ | ✔ |
display: block |
✔ | ✔ | ✔ | ✔ |
display: inline-block |
✔ | ✔ | ✔ | ✔ |
display: contents |
✔ | ❌1 | ✔ | ❌2 |
<ol>
s and<ul>
s are not treated as lists, but<dl>
s perform fine.- 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.
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 |
<th>
s withflex
andgrid
are exposed as cells, not headers.- Using NVDA 2022.1, the table reports the wrong number of rows and columns and cannot be navigated with table navigation commands (Ctrl + Alt + ←↑→↓).
- Inserts text leaf between each list item; NVDA announces each item in the list when navigating by list (L).
- 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.
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 |
- VoiceOver announces the wrong column and row count for each table; VoiceOver table navigation commands do not work (Ctrl + Option + ⌘ + ←↑→↓).
- 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).
- Does not receive keyboard focus; cannot be navigated with VoiceOver button command (Ctrl + Option + ⌘ + J); can be activated with a mouse.
CSS | <tr> , <th> , <td> |
<ul> , <ol> , <dl> |
<h#> |
<button> |
---|---|---|---|---|
display: flex |
✔ | ✔ | ✔ | ✔ |
display: grid |
✔ | ✔ | ✔ | ✔ |
display: block |
✔ | ✔ | ✔ | ✔ |
display: inline-block |
✔ | ✔ | ✔ | ✔ |
display: contents |
✔ | ❌1 | ✔ | ✔ |
<ol>
s and<ul>
s are not treated as lists, but<dl>
s perform fine.
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 |
- VoiceOver treats all cells as in column 1.
- VoiceOver does not announce ordered or unordered lists as lists (description lists are fine).
- 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.
- On this site:
- It’s OK to Use Tables, 16 July 2012
- Hey, It’s Still OK to Use Tables, 1 November 2017
- A Responsive Accessible Table, 2 November 2017
- Tables, CSS Display Properties, and ARIA, 20 February 2018
- Tables, JSON, CSS, ARIA, RWD, TLAs…, 2 April 2018
- Display: Contents Is Not a CSS Reset, 1 May 2018
- Functions to Add ARIA to Tables and Lists, 12 May 2018
- a11yTO Conf: CSS Display Properties versus HTML Semantics, 21 October 2020
- CSS3 — Appendix B: Effects of display: contents on Unusual Elements
- Data Tables by Heydon Pickering
- Short note on what CSS display properties do to table semantics by Steve Faulkner, 4 March 2018
- How display: contents; Works by Ire Aderinokun, 27 Mar 2018
- More accessible markup with display: contents by Hidde de Vries, 20 April 2018, updated to reference this post
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:
- Bug 242779 – AX: display:contents elements are inserted in the wrong position when they have inline renderer siblings, 14 July 2022
- Bug 239479 – AX: Support display:contents for table elements, 18 April 2022
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:
- AX: An unnecessary group is created for every block-flow box with no other useful AX semantics
- Closes new bug Bug 243373 – AX: An unnecessary group is created for every block-flow box with no other useful AX semantics, 30 July 2022
- Also closes its older duplicate Bug 242779 – AX: display:contents elements are inserted in the wrong position when they have inline renderer siblings, 14 July 2022
- AX: On iOS, display:contents elements are inserted in the wrong position when they have inline renderer siblings
- AX: AXCoreObject::textUnderElement always returns an empty string for display:contents elements
- Closes new bug Bug 243486 – AX: AXCoreObject::textUnderElement always returns an empty string for display:contents elements, 3 August 2022
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.
2 Comments
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
In response to .part of my comment was sanitized, so here is a demo that illustrates my point
https://codepen.io/WW3/pen/XWEVoqN
Leave a Comment or Response