Skip to content
DasPeTe Tutorials

Responsive Card Grids with CSS Grid

Build a card layout that adapts to any screen without a single breakpoint, and line up the content of every card with subgrid.

Quest objectives

  • Create a responsive grid with auto-fit and minmax()
  • Understand the difference between auto-fit and auto-fill
  • Let a card span several columns
  • Align the insides of all cards with subgrid

Card grids are everywhere: shops, blogs, level selection screens. They used to need a pile of media queries. With modern CSS Grid, a single line handles nearly everything.

The markup

A list is the right element here, since screen readers will announce how many cards there are:

index.html
<ul class="cards">
  <li class="card">
    <h2>Forest of Whispers</h2>
    <p>A short description of this level.</p>
    <a href="/levels/forest">Play</a>
  </li>
  <!-- More cards -->
</ul>

One line for the whole layout

styles.css
.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
  gap: 1.5rem;
  padding: 0;
  list-style: none;
}

The line with grid-template-columns does all the work. Read it from the inside out:

  • min(16rem, 100%) is the smallest width a card may have. The min() stops a card from overflowing on screens narrower than 16rem.

  • minmax(..., 1fr) lets each column grow from that minimum to an equal share of the free space.

  • repeat(auto-fit, ...) fits in as many columns as there is room for.

Resize your browser window and the columns wrap on their own. There isn't a single media query in sight, and the grid even adapts to the width of its container rather than the screen, so it works inside a sidebar too.

A featured card

To make a card stand out, let it span two columns. The media query is still useful here: on a narrow screen there is only one column, and spanning two would create an extra one and cause overflow.

styles.css
@media (min-width: 40rem) {
  .card--featured {
    grid-column: span 2;
  }
}

Line up the cards with subgrid

Here's a problem you may have seen: one card has a long title, so its text and button sit lower than those of its neighbors. subgrid solves this. A card can take over the row tracks of the parent grid, which means that all titles, texts and buttons in a row share the same lines.

styles.css
.card {
  display: grid;
  grid-row: span 3;
  grid-template-rows: subgrid;
  gap: 0.75rem;
  padding: 1rem;
  border-radius: 0.75rem;
  background: #eef;
}

.card h2,
.card p {
  margin: 0;
}

grid-row: span 3 claims three rows of the parent, one each for the title, the text and the link. grid-template-rows: subgrid then hands those rows to the card's children. Every title row now grows with the tallest title in that row.

Comments

The comments are GitHub Discussions: sign in with GitHub to write one.

The comments come from GitHub, through giscus. Loading them sends your IP address to giscus.app and GitHub.