cft

display: contents and accessibility

There is one CSS property that has a lot of potentials to add flexibility to our layouts but it has some accessibility concerns to keep in mind. This property is display: contents and in this article I tell you what you need to keep in mind before using it


user

Cristian Diaz

2 years ago | 7 min read

Nowadays, we have many options to create layouts, but there is one I consider is overlooked despite having great potential. That property is display: contents and when it's used properly, it's a great tool that adds a lot of layout flexibility, but it has certain accessibility concerns due to browsers' implementation.

This article will explain how to use it properly without harming the site's accessibility.

What is display: contents?

display: contents is a property that allows to remove the container box and makes the element's children behave as if they were its adjacent siblings. I know this is a quite hard concept to understand, so let's use an example. Look at this markup:

<header>

<a href="#">Go to home</a>

<nav>

<ul>

<li>

<a href="#">Clothes</a>

</li>

<li>

<a href="#">Accessories</a>

</li>

<li>

<a href="#">Shoes</a>

</li>

</ul>

</nav>

</header>

When we apply this property to the ul element, it'll act as if weren't there, so the li elements will behave as if they were direct children of nav. The magic of that property resides in all this happening without altering the markup's semantics, so it adds a lot of flexibility when creating the required layout.

With that said, what practical uses can this property have? Let's take a look at that matter.


Example

For this case, I'm going to use an example on my own site. If you check the article section of my site you'll notice a list of articles and a box that mentions which will be the next article (please, let's not talk about the frequency with which I write articles 😅) but there is a catch: despite the “Next article” box is part of the blogs, it's not part of the list of articles itself. At the moment of writing the site's markup, I took this box as a complementary section, and with that in mind, this is the markup I created for this section.

<section class="blog-entries" aria-labelledby="blog-title" id="blog">

<div class="wrapper">

<h2 id="blog-title">Recent blog entries</h2>

<div class="blog-entries-container">

<ul role="list">

<li>

<article class="blog-entries__card">

<!-- article's markup -->

</article>

</li>

<li>

<article class="blog-entries__card">

<!-- article's markup -->

</article>

</li>

<li>

<article class="blog-entries__card">

<!-- article's markup -->

</article>

</li>

<li>

<article class="blog-entries__card">

<!-- article's markup -->

</article>

</li>

</ul>

<aside class="blog-entries__next">

<h3>Next article:</h3>

<p><code>display: contents</code> and accessibility</p>

</aside>

</div>

</div>

</section>

I could add the aside as part of the list, but in my opinion, it's not a part of the published article's list, so I decided to take this approach. The problem with it is that at the moment of layout creation, I wanted this aside to appear right after the first article, which wasn´t possible because of the ul's container box. This is where display: contents come into play. Let's start applying those rules in our stylesheet.

.blog-entries-container {

display: grid;

}

.blog-entries-container ul {

display: contents;

}

.blog-entries-container ul li:first-child {

order: -2;

}

.blog-entries__next {

order: -1;

}

With that code, the blog entries appear in the order I want them to be and the HTML semantics keeps the meaning I wanted to give to this section. Those cases show how display: contents has an interesting utility. However, there are some accessibility risks we need to keep in mind. Let's take a look at them in the next section:


Accessibility concerns

Keyboard accessibility

This is probably the less common situation you are going to find, but it's important to keep it in mind: when you use display: contents in a keyboard interactive elements (like button, a or input) it'll stop being focusable with a keyboard.

This is problematic because as you might know, those elements can be selected by pressing the Tab key for a reason: people with motor disabilities will need to be able to select those elements without the need of a mouse, so the browser adds interactivity to those elements to ensure this kind of user can interact with them. Unfortunately, display: contents will remove that interactivity so it must be avoided at all costs when you use it on interactive elements.

Screen readers accessibility

Unlike keyboard accessibility concerns, screen readers' accessibility can be compromised way more easily.

It all comes back when browsers started implementing display: contents. Maybe there was a mistake in the specs for this property or maybe all browsers have an accidental omission but at the start of the implementation, display: contents did remove the element's semantics, and this is something that took a while to be fixed. The last browser that fixed that problem was Safari in version 15.6, which was released in July of 2022. This is still very little time so it's likely some people haven't updated the browser yet and that could create some accessibility bugs in Safari.

With that in mind, it's valid to ask if it's ok to use this property or not. My answer to that is that it depends. To know it better, make yourself this question: "What would happen if I remove the semantic meaning of the container with display: contents?

Let's come back to my blog container, the worst that could happen is that it'll stop recognizing it as a list, so it'll only show 4 articles without a listing. This is an acceptable way to show the list without compromising particularly the screen reader user's experience, so in my opinion, it's alright to use it, but let's check a case where this would compromise the site's accessibility. Let's check this navigation bar.

Screenshot of a navigation bar with 6 links. It has a green background and it starts with the first three links, then appears the logo in the middle (in that case, a star) and after that, it shows the last three links.

To make this navigation bar, let's start with the markup:

<header>

<svg>

<!-- Logo -->

</svg>

<nav>

<ul>

<li><a href="#">Home</a></li>

<li><a href="#">Asia</a></li>

<li><a href="#">Africa</a></li>

<li><a href="#">America</a></li>

<li><a href="#">Europe</a></li>

<li><a href="#">Oceania</a></li>

</ul>

</nav>

</header>

But we have this problem: nav and ul elements are not letting us use put the li elements at the same level of the logo. So we can use display: contents in those containers:

nav, ul {

display: contents;

}

But this is where the problem comes. Remember: in Safari you still have to deal with the semantics removal, so if you do that, a screen reader will read it as an SVG and six links after it, fully ignoring the list, and even more important, the navigation bar's semantics. Those are the details we still need to check carefully, you just can't assume someone is going to have the last browser version and if that interferes that much with the screen reader's navigation, you need to take some decisions like:

  • Talk with the design area to adjust this content keeping in mind those accessibility concerns.
  • Look up another markup that respects the desired semantic.
  • Testing with screen readers in browser's old versions to check if the user experience is severely compromised.
  • Validate with screen reader users to know their opinion about this components (this is something that should be done always but it's not always in our capabilities)

The good news is that it's solved in Safari's version 16, so those considerations will eventually be a thing from the past. The bad news is that is not the only problem with display: contents. In all browsers, using this property will delete the semantics of the button elements (and remember, you shouldn't use it anyways because of what I mentioned about keyboard accessibility) and Safari only will delete the semantics of the table elements (even on 16 version) so it's recommended to not use it on those elements.


Wrapping up

So, this is display: contents. A property that adds a lot of potential to our designs without compromising the site's semantics. However, there are some considerations to keep in mind so right now we can't use it to its full extent. Luckily for us, eventually those problems will be a thing of the past so keep an eye on this property, you'll surely find a good use for it!

Upvote


user
Created by

Cristian Diaz

Front-End developer with an emphasis in a11y. Occasional tech author for Smashing Magazine and LogRocket. Looking for making the web a more accessible place for everybody.


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles