This is part 1 of 3 in a series of articles about CSS Grid layout and accessibility.
It has already been two years since the first browsers, Chromium 57 and Firefox 52, shipped CSS Grid Layout un-prefixed. Many developers have experimented with it or are using it in production already. More will come as soon as support for Internet Explorer 10 and 11 becomes less important.
Grid offers many ways of building layouts and it challenges us to rethink the way we approach them. This flexibility is great for our development experience, but it may come at the cost of user experience and accessibility if we don’t use it responsibly.
This series of articles will give you an overview of potential implementation pitfalls, or in other words, the dark side of the grid.
- What’s CSS Grid Layout? (in this article)
- Name and theme of this article (in this article)
- Pink Floyd Fun Fact 1 (in this article)
- Compromising on Semantics (in this article)
- Pink Floyd Fun Fact 2 (in part 2)
- Changing Visual Order (in part 2)
- Cross Browser Support
- Pink Floyd Fun Fact 3
- Whose responsibility is it?
- Pink Floyd Fun Fact 4
What’s CSS Grid Layout?
CSS Grid Layout is a grid-based layout system designed for two-dimensional layouts. It’s the first true layout method in CSS. Properties like
display: table were not originally intended for building layouts.
Grid is a great choice if you're not working on just one axis but one two axes.
Of course, there’s Flexbox, but its strength lies in distributing available space and placing items either vertically or horizontally. Flexbox loses a lot of its flexibility as soon as you’re applying
flex-wrap and add widths to your flex items.
This article assumes that you have at least a basic knowledge of CSS Grid Layout. If you're new to the topic, I suggest you check out gridbyexample by Rachel Andrew or Grid Garden before you continue reading.
Name and theme of this article
Now you might think I’m a huge Pink Floyd fan. Well, I’m sorry to disappoint you, I’m not, I just like the design. However, I can’t borrow their design without telling you about them.
Therefore, I present to you: Pink Floyd Fact #1.
Pink Floyd Fun Fact #1
The Dark Side of the Moon is, with over 45 million copies sold, the fourth best-selling album worldwide. Only Back in Black by AC/DC (50 Million), Their Greatest Hits (1971–1975) by The Eagles (51 Million) and, of course, Thriller by Michael Jackson (66 Million) have sold more often.
Compromising on Semantics
Even before grid shipped in any browser, experts like Rachel Andrew were already fearful that developers would compromise on semantics and flatten out document structures to use CSS Grid.
I’ll show you why in a simple example (I know that there are different solutions for this particular task but this is just a demo to illustrate the issue).
Let’s say we have a
section with a heading and a list of items.
<h2>Pink Floyd discography</h2>
<li>The Piper at the Gates of Dawn</li>
<li>A Saucerful of Secrets</li>
<li>Atom Heart Mother</li>
<li>Obscured by Clouds</li>
<li>The Dark Side of the Moon</li>
section forms a 3-column grid, we want the heading to span all columns, and each
li should fill one cell. It should look something like this:
That shouldn't be too hard. We select the
display: grid, add 3 even columns, a
10px gutter and we make the heading span all 3 columns.
grid-template-columns: repeat(3, 1fr);
grid-column: 1 / -1;
And this is what we get:
Doesn’t exactly look as expected. The thing is, only direct children of the grid container will align with the grid. In our example, those are the
h2 and the
ul – but we want all the
li elements to fill cells in the grid.
Okay, let’s try to fix that.
PS: I'm well aware that I could apply
display: grid to the
ul directly but this is just a simplified example. There are other (more complicated) use cases where you can't do that easily.
Solution #1: Flattening the document structure.
If the placement algorithm only affects direct child elements, we’ll just make our
li direct children by removing the
ul and swapping the
divs to avoid invalid HTML. This is a solution, but it’s a bad solution because we’re compromising on semantics for design reasons.
<h2>Pink Floyd discography</h2>
<div>The Piper at the Gates of Dawn</div>
<div>A Saucerful of Secrets</div>
<div>Atom Heart Mother</div>
<div>Obscured by Clouds</div>
<div>The Dark Side of the Moon</div>
Flattening the document structure may have bad effects on the semantics of your document, which is especially bad for screen reader users. For example, when you’re using a list, screen readers usually announce the number of list items which helps with navigation and overview.
Also, a flat document might be harder to read when displayed without CSS.
Wait! What? Why would someone disable CSS?
It’s unlikely that users disable CSS on purpose but sometimes an error occurs or the connection is just so slow that only the HTML displays successfully. If you’ve ever been on vacation in Italy and had to use public WIFI, you know what I’m talking about.
Please don’t flatten document structures.
Solution #2: Creating a subgrid
The arguably best solution would be to use subgrids. A grid item can itself be a grid container with its own column and row definitions. It can also be a grid container but defer the definitions of rows and columns to its parent grid container.
By setting the value of
subgrid on the unordered list, the list items now align with the parent grid. This is super cool!
Use subgrids as soon as they’re available.
Solution #3: Using display: contents
An alternative to using subgrids is a different property that has a similar effect. If you set the
display value of an element to
contents, it will act as if it got replaced by its child items.
In our example, this causes the list items to take part in the alignment of the sections grid because for them the parent
ul doesn’t exist anymore. This is exactly what we want, and it works perfectly fine but, (yeah I’m sorry, there’s a but) Edge doesn’t support it.
The lack of support per se isn’t the issue but rather why it’s not supported. There’s a bug in Chrome, Opera, and Safari that removes an element with a
display value of
contents from the accessibility tree, making it inaccessible to screen reader users. It’s like applying
display: none – the element just doesn’t exist anymore for assistive technology.
Microsoft Edge will consider adding the feature as soon as blink and webkit-based browsers fix the bug. Consider not using
contents until then.
Solution #4: Nesting grids
As already mentioned, a grid item can also be a grid container. We can select the unordered list, make it span the whole width, and inherit values from the parent grid.
grid-column: 1 / -1;
Nesting grids isn’t a perfect solution and sometimes it might not work, but in this simple example it’s good enough.
The situation regarding subgrids is anything but perfect. The
subgrid value isn’t a standard yet,
display: contents is buggy, and nesting grids will only work in specific use cases. If you see yourself compromising on semantics just to use CSS Grid Layout, don’t use it or try to workaround the problem until browsers fix the
display: contents bug or ship subgrids.
This was part 1 of the dark side of the grid. In part two I’ll show you how easy it is to confuse users unintentionally, why it’s bad, and how to avoid it.
- The Story of CSS Grid, from Its Creators by Aaron Gustafson
- Modern CSS Layout, power and responsibility by Rachel Andrew
- CSS Grid Level 2: Here Comes Subgrid by Rachel Andrew
- CSS Grid Layout Module Level 2
- Grid vs. Subgrid: An Elemental Example by fantasai
- Display: Contents Is Not a CSS Reset by Adrian Roselli
- Add display:contents #608
- Pink Floyd photo