Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Subgrid

A grid aligns its direct children. But a card has its own elements inside: a heading, text, a button, and they know nothing about the neighbouring cards. If one card has a two-line heading, its button slides lower than the neighbours' buttons. Subgrid solves this: a nested grid borrows rows or columns from the parent one.

The problem

Three cards in a grid, each with its own inner grid. The headings differ in length, and everything below them gets out of line:

<style>
  .cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
  .card { display: grid; gap: 8px; padding: 12px; background: lavender; }
  .card h3 { margin: 0; font-size: 16px; }
  .card p { margin: 0; }
</style>
 
<div class="cards">
  <div class="card"><h3>Cappuccino</h3><p>Espresso and milk foam.</p><button>Add to cart</button></div>
  <div class="card"><h3>Raf with vanilla sugar and cream</h3><p>Soft and sweet.</p><button>Add to cart</button></div>
  <div class="card"><h3>Latte</h3><p>Lots of milk.</p><button>Add to cart</button></div>
</div>

The text and button of the second card sit lower than the neighbours': each card counts its rows on its own.

grid-template-rows: subgrid

The fix is two lines. The card takes three rows in the parent grid (grid-row: span 3), and instead of its own rows uses those three: grid-template-rows: subgrid:

<style>
  .cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
  .card {
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    gap: 8px;
    padding: 12px;
    background: lavender;
  }
  .card h3 { margin: 0; font-size: 16px; }
  .card p { margin: 0; }
</style>
 
<div class="cards">
  <div class="card"><h3>Cappuccino</h3><p>Espresso and milk foam.</p><button>Add to cart</button></div>
  <div class="card"><h3>Raf with vanilla sugar and cream</h3><p>Soft and sweet.</p><button>Add to cart</button></div>
  <div class="card"><h3>Latte</h3><p>Lots of milk.</p><button>Add to cart</button></div>
</div>

Now the heading row has the same height in every card: that of the longest one. The text and buttons line up.

How it works

The rows now belong to the parent .cards, and the card only places its children on them. The browser computes the height of each row across all cards at once. That is why the number in span must match the number of card parts: three parts — span 3.

Card propertyWhy
display: gridthe card is a grid too
grid-row: span 3take three rows of the parent
grid-template-rows: subgriduse those rows as its own
gapits own gap between the parts

A subgrid inherits gap from the parent, but it can be overridden, as in the example.

Columns too

grid-template-columns: subgrid aligns columns. This is how a form is made where all labels have the same width, even though each "label and field" pair is in its own block:

<style>
  .form { display: grid; grid-template-columns: max-content 1fr; gap: 8px; max-width: 400px; }
  .row { display: grid; grid-column: span 2; grid-template-columns: subgrid; align-items: center; }
</style>
 
<div class="form">
  <div class="row"><label for="n">Name</label><input id="n"></div>
  <div class="row"><label for="e">Email address</label><input id="e"></div>
</div>

max-content makes the first column as wide as the longest label, and both fields start on the same line.

Support

Subgrid works in all major browsers since 2023. An older browser does not recognize subgrid, the card becomes a regular grid, and the layout looks like the first example — uneven, but not broken.

Common mistake

Forgetting grid-row: span 3. The card takes one row in the parent, and the subgrid gets only that one. The extra parts have no room — they pile on top of each other:

<style>
  .cards { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .card { display: grid; grid-template-rows: subgrid; padding: 12px; background: mistyrose; }
  .card h3, .card p { margin: 0; }
</style>
 
<div class="cards">
  <div class="card"><h3>Cappuccino</h3><p>Espresso and foam.</p><button>Add to cart</button></div>
  <div class="card"><h3>Latte</h3><p>Lots of milk.</p><button>Add to cart</button></div>
</div>

subgrid always goes together with a span over the right number of rows or columns.

Thanks for reading!

Donate to the site — we work hard for you.