Display: Contents Is Not a CSS Reset

May 1, 2018 ; 0 Comments

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?

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:

Perhaps more interesting is the collection of special cases:

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.

Using NVDA and Firefox 59.02

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.

Chrome’s accessibility tree showing an <h2> as it normally appears and then after display: contents has been applied. It says the elements is ignored and Accessibility node not exposed.

Chrome’s accessibility tree showing a <table> as it normally appears and then after display: contents has been applied. It says the elements is ignored and Accessibility node not exposed.

Chrome’s accessibility tree showing a <ul> as it normally appears and then after display: contents has been applied. It says the elements is ignored and Accessibility node not exposed.

Chrome’s accessibility tree showing a <button> as it normally appears and then after display: contents has been applied. It says the elements is ignored and Accessibility node not exposed.

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

Safari bug 39630240 (which I cannot see because my AppleID may not have the needed permissions to see it)

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?

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 Stefan Judis (@stefanjudis) April 30, 2018

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… Amelia Bellamy-Royds (@AmeliasBrain) April 30, 2018

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… Marco Zehe (@MarcoInEnglish) April 30, 2018

tl;dr: for me, _the_ use case for display:contents is increased accessibility, which now works nowhere, because of these bugs. Hidde (@hdv) April 30, 2018

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. 🌺Taudry Hepburn🌺 (@tabatkins) April 30, 2018

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… James Steinbach (@jdsteinbach) April 30, 2018

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. Amelia Bellamy-Royds (@AmeliasBrain) April 30, 2018

Indeed there are plenty of sensible reasons to have elements you might not want visually to create a box. Rachel Andrew (@rachelandrew) April 30, 2018

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! SelenIT (@SelenIT2) April 30, 2018

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.

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).

♠ “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… Adrian Roselli (@aardrian) October 16, 2018

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.

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):

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:

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.

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.