Display: Contents Is Not a CSS Reset
CSS resets are a collection of CSS styles that undo the default browser styling of many or most HTML elements.
Recently I have seen cases of developers using display: contents
on lists and headings to remove the margins and padding, and generally to visually do what a CSS reset might do. Essentially, they are using display: contents
as a quick and dirty CSS reset.
This is dangerous for accessibility.
I explain why through the following sections of this post, because if I had to research it to be sure then you damn well have to read it.
- What is display: contents?
- Those Accessibility Implications
- Browser and Spec Bugs
- Tweets
- Wrap-up
- Update: October 17, 2018
- Update: October 22, 2018
- Update: June 21, 2019
- Update: June 23, 2022
- Update: July 5, 2022
- Update: 20 December 2022
- Update: 2 August 2024
- Update: 5 September 2024
What is display: contents?
It might help to identify what we are talking about. You can skip this part if you are already familiar (it goes on for a bit).
From Developers
In its simplest form, display: contents
exists to visually remove the element’s box and replace it with its content. Essentially, it treats an element as if the element’s opening and closing tags were removed and the content was left naked on the page.
This can have value when applying it to a <div>
-soup page that you want to use CSS grid or flex to lay out. Perhaps you have inherited a Bootstrap page with its rivers of <div>
s and you want to progressively enhance it to use CSS grid.
Ire Aderinokun provides a high-level overview in her post How display: contents; Works, though browsers do not do quite what she asserts.
Hidde de Vries also explains it in his post More accessible markup with display: contents. His post does not, however, look at it quite the way I have seen display: contents
in play.
Per W3C
The CSS 3 specification provides guidance as well:
The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes and text runs as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced in the element tree by its contents (including both its source-document children and its pseudo-elements, such as
::before
and::after
pseudo-elements, which are generated before/after the element’s children as normal).Note: As only the box tree is affected, any semantics based on the document tree, such as selector-matching, event handling, and property inheritance, are not affected.
This value behaves as
display: none
on replaced elements and other elements whose rendering is not entirely controlled by CSS; see Appendix B: Effects of display: contents on Unusual Elements for details.
Perhaps more interesting is the collection of special cases:
<br>
<wbr>
<meter>
<progress>
<canvas>
<embed>
<object>
<audio>
<iframe>
<img>
<video>
<frame>
<frameset>
<input>
<textarea>
<select>
display: contents
behaves asdisplay: none
.<legend>
Per HTML, a
legend
withdisplay: contents
is not a rendered legend, so it does not have magical display behavior. (Thus, it reacts todisplay: contents
normally.)<button>
<details>
<fieldset>
These elements don’t have any special behavior;
display: contents
simply removes their principal box, and their contents render as normal.- any other HTML element
Behaves as normal for
display: contents
.
Those Accessibility Implications
Today browsers will take an element with display: contents
and drop it from the accessibility tree. If you read my piece on adding table semantics back into <table>
elements with ARIA (after CSS display properties were applied), well, that won’t work here. Not even a tiny bit.
Demo You Can Try
I have embedded a CodePen below, though it is easier to test the debug version as it dumps all the CodePen wrapper code.
See the Pen Table with display:contents by Adrian Roselli (@aardrian) on CodePen.
Screen Reader Example
I walked through it with NVDA and Firefox to demonstrate it in action. I am trying to navigate by table (T), list (L), button (B), and heading 2 (2). None of them are recognized. It may be worth noting that each of those elements has an ARIA role applied that matches its native role.
Since I made this video I amended the CodePen to contain two <button>
s, one with an onkeypress
event handler and tabindex="0"
to show that it is dead to keyboard users. I did this because you can still click on the other <button>
and it will fire an onclick
event.
Accessibility Tree
To disabuse you of the notion that this is the fault of screen readers, I can assure you that none of the element’s information (including ARIA) makes it to the screen reader. These screen shots are from Chrome 66.
Browser and Spec Bugs
Ten days before my tests, Hidde de Vries had already filed bugs with browsers based on use of display: contents
in grid layouts:
- Firefox bug 1455357: Setting grid item to display:contents resets its accessible role
- Update, 15 May 2018: Verified fixed in Firefox 62.0a1 (20180515220059).
- Update, 22 October 2018: fixed only for lists, as narrowly defined in the original bug.
- Chromium Issue 835455: Element not exposed to accessibility tree when it is set to display: contents
- Update, 7 March 2021: closed as fixed.
- Safari bug 39630240 (which I cannot see because my AppleID may not have the needed permissions to see it)
- Update, 13 June 2022: still cannot see it, but Apple Evangelist Jen Simmons asserts this is fixed (I cannot be sure if it is this bug, though).
- Update, 5 July 2022: it is not fixed.
- Bug 193567 – ::before/::after elements not filling their grid cell when container has display: contents, a WebKit bug loosely related to this.
- Update, 30 October 2019: bug closed
- Bug 188259 – Toggling “display: contents” to “display: none” fails to hide the element , a WebKit bug also loosely related to this.
- Update, 25 March 2019: bug closed
After a Twitter conversation this morning with Ilya Streltsyn, he took the initiative to file an issue against the CSS spec:
- CSSWG #2632: [css-display][css-aam][selectors-4] How elements with `display:contents` get focused?
- Update, 5 August 2021: closed as out of scope.
Tweets
I don’t believe I am the first to have noticed display: contents
used as a CSS reset, but my tweets this morning seemed to catch some folks off guard and also give me some insight into issues.
Oh wow – this discovery makes `display: contents` useless for dealing with lists and other things. 😲 twitter.com/aardrian/statu… #a11y
This is bad behavior by browsers, but until it is fixed, the only acceptable use for `display: contents` is to remove extra divs that you added for your fallback layout but don't need for your grid layout.
Do not use—yet—on semantic elements: <ul>, <nav>, <button>, <header>, etc twitter.com/aardrian/statu…
Interestingly enough, some other change to a web site *after* it was loaded will make the stuff reappear somehow, at least in Firefox. @jcsteh investigated this just the week before last, and we have a bug in Bugzilla under investigation for this problem. Whole Thread: 👇 twitter.com/aardrian/statu…
tl;dr: for me, _the_ use case for display:contents is increased accessibility, which now works nowhere, because of these bugs.
It… It removes it from the CSS box tree too. It has all sorts of bad side effects if you try to use it like that.
Not only does using `display: contents` as a element-reset hack create a major #a11y probem, it's also poorly supported.
Try `all: initial` instead. It resets an element's styles AND it's much better supported:
caniuse.com/#feat=css-all
caniuse.com/#feat=css-display-contents
twitter.com/aardrian/statu…
If the browsers implemented it as it was intended, it would be very useful, allowing you to have semantic markup wrappers that are independent from your layout.
e.g. a <nav> list followed by a search bar and a language selector, laid out as a single grid or flex container.
Indeed there are plenty of sensible reasons to have elements you might not want visually to create a box.
Yes, currently it is broken. Thank you for the excellent point about `display:contents` elements getting focused! I opened an issue in the CSSWG repo about it: github.com/w3c/csswg-draf…, will file bugs for browsers as soon as the spec clarifies this!
Wrap-up
For now, please only use display: contents
if you are going to test with assistive technology and can confirm the results work for users.
Update: October 17, 2018
You cannot assume that it is safe to use display: contents
without weighing the risks. Chrome and Safari still dump semantics. The Firefox fix is only a recent update, so Firefox users who were told by screen reader vendors to grab the ESR (which is based on 52) due to screen reader issues are also still impacted.
Given that very public knowledge, some bad advice might be to say: bugs are temporary and browsers are updated all the time, so I won’t need to be cautious for long.
Well, you have to be cautious until the long tail of these browsers has disappeared for your audience.
This update brought to you by my piss-take (below) on an unfortunate tweet (with its missing image alt text).
♠ “When to use display:contents to improve semantics in your HTML.” Working with CSS Grid occasionally come at a cost and that is semantic markup.
stuffandnonsense.co.uk/blog/when-to
♠ “Do not use display: contents as it removes semantics from your HTML.” Working with
display: contents
will ruin native HTML semantics in most browsers.adrianroselli.com/2018/05/displa…
As an aside, I had that photo taken by Jenny Hiseler at the #a11yTOConf after-party, mocked it up and tweeted it that night, and it appeared unexpectedly in Manuel Matuzovic’s slides the next morning. The shirt was a happy coincidence, the weird expression on my face was not.
Update: October 22, 2018
Because I am generally not working with clients on the bleeding-edge stuff, and because I am not using display: contents
, Scott picked out that Firefox did not, in fact, fix the display: contents
universally.
In his post Unbuttoning Buttons he has a great demo which shows how it falls down. I gathered some feedback on the Twitters, and then filed a new bug (not the best name, was juggling calls):
- Bug 1500958: display: contents removes attributes from a11y tree (such as button)
- Update, 21 June 2019: marked resolved
With TPAC going on this week, I know this came up in conversation as well, so the timing is good.
There are two other related bugs:
- Bug 1494196: Element with ARIA role and display: contents doesn’t get an accessible (September 25, 2018)
- Bug 1500416: Dynamic insertion of display: contents elements doesn’t work (October 19, 2018)
- Update, 22 October 2018: marked resolved
Firefox 64 Nightly also shows this bug. Please avoid display: contents
, especially since we now know that even in the one browser that claimed to fix it, it is still broken.
Update: June 21, 2019
Firefox has fixed the bug display: contents removes attributes from a11y tree (such as button). In a very quick test, all looks to be good.
Update: June 23, 2022
Apple has been the laggard on this, but Apple Evangelist Jen Simmons asserts this is fixed on June 13, 2022:
I’m so happy that the severe mistake implementing `display: contents` and how it impacts the accessibility tree — one that originally shipped in all browsers — is now finally fixed in all browsers.
(Including on iOS! Ooops my bug on Can I Use.) pic.twitter.com/IGrvMTM1Rv
The Can I Use page on display: contents
she references in the tweet.
Update: July 5, 2022
I should not have shared that Safari news without testing. Especially since back in March, Safari 15.4 also promised display: contents
would be fixed:
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.
That paragraph is not in the current version of the Safari 15.4 release notes. For whatever reason, the fix was not deployed.
Fast forward to July and so far the display: contents
fix does not appear to be in Safari Technology Preview 148 (Safari 16), the latest release at the time of writing, and its release notes only discuss it in the context of inert
.
In fact, it looks like tables are still broken with any display property.
Lists are the only ones that fared well from my 2020 list of tests, possibly because VoiceOver always ignores bullet-less lists, and the description list seems to behave outside ordered and unordered list logic.
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
anddisplay: grid
are ignored). This restriction is specified in the HTML Standard (The2) and implemented in all browsers. It is possible to work around this limitation with<details>
element is expected to render as a block boxdisplay: contents
3. […]
- developer.mozilla.org/docs/Web
/HTML/Element /details - html.spec.whatwg.org/multipage
/rendering.html #the-details-and-summary-elements - 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.
Update: 2 August 2024
CSS and reading order are still an ongoing concern 9 years after I wrote this post, as demonstrated in this CSS Day talk. Pay attention to the section related to display: contents
, at roughly 36:30.
YouTube: Layout and Reading Order | Rachel Andrew | CSS Day 2024, 50:46
Then go read Request for developer feedback on reading-flow and elements with display: contents. If you work in accessibility in particular, please weigh in. Part of the reason display: contents
is such a mess is because it was poorly specified and the needed digital accessibility expertise was not part of that process.
Update: 5 September 2024
Ahmad Shadeed discusses display: contents
in a new post and specifically notes accessibility concerns:
CSS
display: contents
is known to cause accessbility issues when used with HTML tables, headings, buttons, and lists. Please make sure to avoid using it on such elements. Here are some further resources:
Those four things he cites are simply what I tested. It required too much effort on my part to test everything, so I used a sub-set of HTML elements that covered a good range of features.
More direct advice would be: do not use display: contents
on anything that can take focus or is interactive. Probably avoid it on things with richer semantics (such as tables and lists and headings and so on).
Update: 24 November 2024
I’ve updated my post It’s Mid-2022 and Browsers (Mostly Safari) Still Break Accessibility via Display Properties twice this week to note two cases of Safari breaking with display: contents
in web components.
3 Comments
I don’t understand where I would ever want to use this. I’d modify html instead.
In response to .Example use case post: https://hidde.blog/more-accessible-markup-with-display-contents/
In response to .FWIW, I reference Hidde’s post in the From Developers section along with a link to Ire Aderinokun’s post:
Hidde de Vries also explains it in his post More accessible markup with display: contents. His post does not, however, look at it quite the way I have seen
display: contents
in play.
Leave a Comment or Response