Why display: contents is not CSS Grid Layout subgrid

You won’t get far through a conversation about subgrid in CSS Grid Layout without someone suggesting that display: contents solves most of the problems anyway, so do we really need subgrid? This really isn’t the case, display: contents does indeed solve a class of problems, but these are different problems to those that subgrid would help us with. In this post I take a look at the different use cases.

If these features are new to you, I have written previously about the very useful display: contents which is in Firefox and is in the process of being implemented in Chrome. I’ve also written about the dropping of the subgrid feature from the Level 1 CSS Grid Layout specification. These posts would provide some useful background context to this one.

My example layout

This is the sort of layout you get from someone who lives under the blissful assumption that content will emerge from the CMS with perfectly even line lengths.

Card design

At first glance this looks like a pretty straightforward grid layout, which we can create with just a few lines of code.

.grid {
  display: grid;
  max-width: 960px;
  margin: 0 auto;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 20px;
}

However, when the real world of content happens to our layout we end up with something like this:

With real contents the cards are untidy

The items inside each card are not direct children of the grid container and therefore we have no way to line up the internal elements with each other.

The best I can do is to make each card a grid container itself, or a flex item and use that to push the foster down to the bottom. This is a bit neater but what I really would like is for the card internals to also line up in rows.

The subgrid feature that was removed from the level 1 specification would allow us to achieve this. I can’t show you working code, but I can explain how it would work with our design.

Each card has four child elements, making four rows. The title, the image, the content and the footer.

The elements with a class of .card are a direct child of the grid container, so they participate in grid layout - meaning that our cards display in rows and columns. We need four rows for the elements in our card, so I set the card to span four rows of the grid. We would then need to set the card itself to display: subgrid, this would mean that the four child elements of our card would now participate in the parent grid layout and slot one into each row. As each card would do the same, the rows across the entire grid would line up.

.card {
  border: 4px solid rgb(24,154,153);
  background-color: #fff;
  grid-row: auto / span 4;
  display: subgrid;
}
What subgrid might give us

There are no implementations of this. I based my example code on the proposal that was removed from level 1, already a revised and simplified proposal from earlier versions of the spec. In reality we would need to do a little more work to get gaps round the cards and not the internal rows, however we could get our internal rows aligned with each other fairly easily.

Can display: contents achieve the same thing?

What display: contents does is remove the box of the element it is applied to. This is useful if you have an element required for semantic rather than visual purpose and want its children to participate in a grid or flex layout.

If you take the following example, of two sets of HTML. They are identical, other than that one has a class of flex and the other grid.

<div class="flex">
  <div>One</div>
  <div>Two</div>
  <div class="nested">
        <div>Nested One</div>
        <div>Nested Two</div>
  </div>
</div>

<div class="grid">
  <div>One</div>
  <div>Two</div>
  <div class="nested">
        <div>Nested One</div>
        <div>Nested Two</div>
  </div>
</div>

We can make two very simple layouts, one for flex and one for grid. I am adding borders to the container, and the direct children of the container.

.flex {
  display: flex;
  border: 8px solid rgb(3,99,143);
}

.flex > * {
  flex: 1;
  border: 8px solid rgb(24,154,153);
}

.grid {
  display: grid;
  border: 8px solid rgb(3,99,143);
  grid-template-columns: 
      repeat(4,minmax(200px, 1fr));
  grid-gap: 8px;
}

.grid > * {
  border: 8px solid rgb(24,154,153);
}

You can see how only the direct child elements become part of the grid or flex layout. The nested elements display in block flow.

Nested items are not part of flex or grid layout

The items that contain nested elements have a class of nested. We can target that and see what happens if we change the display value of those elements to contents.

.nested {
  display: contents;
}

If you are using Firefox or Chrome Canary you will see that the nested items now begin to participate in the grid and flex layouts, as the box of their parent has disappeared meaning that they act as if they were direct children of the flex or grid item.

Simple display:contents example

However, they have not become direct children. I used a selector that would only target direct children to apply borders and add the flex property. Our items have no border and the flex items have not had flex:1 applied so are using the default flex properties.

You can also see that the border on the item with display: contents has gone, as the box has gone. You cannot apply backgrounds and borders to an item with display: contents. This then, is a big reason why display:contents can’t patch a lack of subgrid. If we return to our earlier example with the cards that we want to line up, we could apply display: contents to the card, which is the direct child of the grid.

.card {
  border: 4px solid rgb(24,154,153);
  background-color: #fff;
  display: contents;
}

However, as we are using auto-placement to lay out our cards, once the outer box is removed the internal items from each card now participate in grid layout and so are auto-placed. We now get the title in the first grid cell, then to the right the image, then the contents, then we move onto the next row for the footer. Then the next card begins.

display:contents removes the box

You could change the flow of these to column, which would stack them up but then you would need to decide how many rows there were in order to force the wrapping into columns.

To get anywhere close to the layout we hoped for we are going to need to place each item on the grid, ensuring that the parts of our card are placed where we want them. This is the CSS required for one card:

.card:nth-child(1) h2{ 
  grid-column: 1; 
    grid-row: 1; 
}
.card:nth-child(1) img{ 
  grid-column: 1; 
    grid-row: 2; 
}
.card:nth-child(1) .inner{ 
  grid-column: 1; 
    grid-row: 3; 
}
.card:nth-child(1) footer{ 
  grid-column: 1; 
    grid-row: 4; 
}

You can see the full worked example in the following CodePen - you need to use Firefox or Chrome Canary to see the display as in the screenshot below.

Note that this actually doesn’t get us what we want. We have lost the background and borders and spacing around the cards. We have lost auto-placement and the ability to have as many rows of cards as are needed. This is not a good use of display: contents and is exactly why we need sub grid in Grid Layout.

Using display contents on the cards

When should you use display: contents?

Once we have implementations in all our browsers I think there will be plenty of great uses for display: contents. The time to use it is when the element you are removing has no box styling - backgrounds, borders and so on. It should help us to maintain good semantic structure, while also being able to get the layouts we want. Let’s hope we also get subgrid support soon, as between these two features we’ll add a great deal more power to our grid layouts.

To help encourage adoption of display: contents in more browsers then you could head over to Edge UserVoice and give the feature an upvote. The subgrid feature has been moved to Level 2 of CSS Grid Layout. If you have thoughts and use cases for subgrid then please do write those up, if you ping me about them I’ll add them to my list for when this is discussed. Remember that it is while a specification is in the early stages that you get the chance to have your requirements be part of the discussion and hopefully then solved by the resulting spec.

The CSS Layout Online Workshop

If you are ever baffled by floats, puzzled by collapsing margins or want to understand what is happening under the hood of a framework, this course is for you.

♡ Likes

↺ Reposts

🗨 Comments

keith•j•grant
keith•j•grant on the 20 Jul 2017:

I can see grid-gap getting in the way in subgrids. I suppose using margin on certain grid items instead would be the way to handle it?

HJ Chen
HJ Chen on the 21 Jul 2017:

do read the linked articles on subgrid and display: contents, especially if u haven't tried out grid or display: contents before
#css

Ethan Marcotte
Ethan Marcotte on the 21 Jul 2017:

I’d missed this last year, Rachel—thanks so much for re-upping it. I’d never considered that perspective. (And couldn’t agree more with it.)

Posted a response? Enter the URL

This site uses Webmention. If you post a response to this post on your own site, and you also support Webmention I'll be notified automatically. If not you can add a link here.