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?

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 as display: none.

<legend>

Per HTML, a legend with display: contents is not a rendered legend, so it does not have magical display behavior. (Thus, it reacts to display: 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.

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. Chrome’s accessibility tree showing an h2 with display contents.
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. Chrome’s accessibility tree showing a table with display contents.
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 list. Chrome’s accessibility tree showing a list with display contents.
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. Chrome’s accessibility tree showing a button with display contents.
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:

After a Twitter conversation this morning with Ilya Streltsyn, he took the initiative to file an issue against the CSS spec:

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.

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.

No comments? Be the first!

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>

This site uses Akismet to reduce spam. Learn how your comment data is processed.

To respond on your own website, enter the URL of your response which should contain a link to this post's permalink URL. Your response will then appear (possibly after moderation) on this page. Want to update or remove your response? Update or delete your post and re-enter your post's URL again. (Learn More)