The example that made me get CSS subgrid

Some new CSS features are hard to put in practice, especially when there are some (not so painful) workarounds. CSS subgrid is different, learn it and you will never do without it ever again.

Author Sandro Maglione

Sandro Maglione

Contact me

I (finally) found an extremely satisfying example of using subgrids in CSS. It's also practical and common for a variety of layouts.

It's what made CSS subgrid click for me. Here is how it works 🪄

Design: Payment plan selection

Most (all) product websites have a payment plan selection page. And most (all) display a "recommended" plan.

I have been working on one such layout. The design has a "Recommended" label on top of only the recommended plan. You still want the plans to be horizontally aligned, only the label should float above one plan.

But the label makes one card taller than the others by default:

$3.99

$3.99

$3.99

$3.99

One option for this simple template is to use align-items: end:

$3.99

$3.99

$3.99

$3.99

But then the layout breaks the moment we add a description inside the card (as it's the case when the designer realizes the mistake):

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad

$3.99


Lorem ipsum dolor sit amet consectetur

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad velit labore, minus eos

$3.99


Lorem ipsum dolor sit

What we want instead is to have all the cards aligned and with the same height, and the label "floating" on top of only one card.

Some solutions include:

  • Make the label position: absolute inside the position: relative card and use top: 0 + transform: translateY(-100%) to move it up. This breaks the layout, since the label is now outside the card
  • Using negative margins (somehow) to push the label up. This also breaks the layout (don't do this)

Subgrid is the professional solution to this problem, and it's also elegant and satisfying.

Anatomy of a subgrid

First: the card's container must have layout: grid. A subgrid inherits the parent configuration, so grid is required.

Second: subgrid is a property of grid-template-rows and grid-template-columns, therefore the card must have layout: grid as well, with grid-template-rows: subgrid.

<div className="grid-container">
  <div className="subgrid-container">
    <p>Recommended</p>
    <div className="card-container">{/* Card */}</div>
  </div>
</div>
.grid-container {
  display: grid;
}

.subgrid-container {
  display: grid;
  grid-template-rows: subgrid; /* 👈 */
}

But, this is not yet all. This is where I tripped up. The layout gets all squashed together in a single row.

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad

$3.99


Lorem ipsum dolor sit amet consectetur

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad velit labore, minus eos

$3.99


Lorem ipsum dolor sit

That's because adding grid to the card makes all its content fit in a single cell by default, therefore the label and content are overlapping.

We need to explicitly add grid-row: span 2 to make space for two rows inside the card container. This is the magic sauce ✨

.grid-container {
  display: grid;
}

.subgrid-container {
  display: grid;
  grid-row: span 2; /* 👈 */
  grid-template-rows: subgrid;
}

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad

$3.99


Lorem ipsum dolor sit amet consectetur

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad velit labore, minus eos

$3.99


Lorem ipsum dolor sit

Nearly there! Now each column has two rows, but by default the content starts at the first row. The very last trick is adding grid-row-start: 2 to the card container to make it start at the second row.

.grid-container {
  display: grid;
}

.subgrid-container {
  display: grid;
  grid-row: span 2;
  grid-template-rows: subgrid;
}

.card-container {
  grid-row-start: 2; /* 👈 */
}
<div className="grid-container">
  <div className="subgrid-container">
    <p>Recommended</p>
    <div className="card-container">{/* Card */}</div>
  </div>
</div>

Now the label sits on its own first row, shared between all the cards from the parent grid, and the content is aligned and stretched in the second row.

Try to analyze the layout below using DevTools. The card is marked as subgrid, and the label sits on its own row 👇

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad

$3.99


Lorem ipsum dolor sit amet consectetur

$3.99


Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex doloribus similique ad velit labore, minus eos

$3.99


Lorem ipsum dolor sit


That's the beauty of CSS subgrid. Every time you need to align content in between grid items, reach for subgrids. Its elegance and simplicity are unmatched 🤌