Skip to Content

Auto-fit 3 cards with content aligned in subgrid

Introduction

CSS Grid helps you manage complex parallel arrangements. Instead of wrangling floats or stacking endless flex containers, you define a grid and subgrid(s) to let the browser do the heavy lifting.

I created a downloadable demo so you can play with this setup, one example “as-is”, the other with a centered third element when wrapping occurs for hand held devices.

Demo: Three card layout using auto-fit
Demo: Three card layout using auto-fit

The core grid setup

Three card layout grid diagram A visual representation of a CSS grid with three cards in a row, each labeled Card 1, Card 2, and Card 3. Card 1 Card 2 Card 3
.team-wrapper {
  grid-template:
    repeat(3, auto) /
    repeat(auto-fit, minmax(min(100%, 18em), 1fr));
}
  • display: grid; turns .team-wrapper into a grid container.
  • grid-template: is shorthand for grid-template-rows / grid-template-columns.
  • repeat(3, auto) means three rows, each sized automatically to fit their content.
  • The columns use repeat(auto-fit, minmax(min(100%, 18em), 1fr)):
  • auto-fit fills the row with as many columns as will fit.
  • minmax(min(100%, 18em), 1fr) ensures each card is at least 18em wide, but can shrink to fit smaller screens, and grow to fill available space.

Subgrid, keeping card content aligned

This creates a nice, simple layout using subgrid that would have caused endless headaches not so long ago.

Card internal grid diagram A diagram showing a card layout: left area with a dashed box labelled Image, right area with two stacked dashed boxes labelled Header and Details. Image Header Details
.team-card {
  grid-row-end: span 3;
  grid-template:
    subgrid/
    min(5em, 33%) 1fr;
}
  • grid-row-end: span 3; makes each card span all three rows of the parent grid, so content lines up across cards.
  • grid-template: subgrid/… means the card inherits the row structure from .team-wrapper.
  • The columns inside each card are set to min(5em, 33%) 1fr, so the image and text always stay in proportion.
.team-card > :not(img) {
  grid-column: 2;
}

.team-card img {
  grid-area: 1/ 1 / span 3;
}
  • .team-card > :not(img)
    • Selects all direct children of .team-card that are not <img> elements.
    • Applies grid-column: 2;, which places these elements in the second column of the card’s grid.
    • Effect: Ensures that all content except the image (such as headings, roles, and details) appears in the right-hand column of the card layout.
  • .team-card img
    • Selects all <img> elements that are direct children of .team-card.
    • Applies grid-area: 1 / 1 / span 3;, which places the image in the first column and spans it across all three rows of the card’s grid.
    • Effect: The image always appears on the left side of the card, vertically spanning the entire card, while the rest of the content is aligned to the right.
.team-details {
  display: grid;
  grid-gap: inherit;
  align-content: start;
}
  • .team-details
    • Applies display: grid;, making the details section a grid container for its child elements.
    • Uses grid-gap: inherit; to set the gap between grid items, inheriting the value from its parent (for consistent spacing).
    • Sets align-content: start; so the grid items are packed at the top of the container, rather than stretching to fill the available space.
    • Effect: Ensures the details section is neatly spaced, with justified text and content aligned to the top, for a visually tidy card layout.

Centring the last card, before and after

Ever noticed how, when you have three cards and the grid wraps to two on the first row and one on the second, the last card hugs the left edge? Not ideal.

Three card layout before centring the second row A diagram showing three cards in a CSS grid: Card 1 and Card 2 are in the first row, Card 3 is in the second row, not centred. This visual is used to illustrate the state before applying centring to the second row in a responsive layout. Card 1 Card 2 Card 3

If the automated flow, with the third element wrapping to the left just won’t do then…

Here’s the fix;

Three card layout after centring the second row A diagram showing three cards in a CSS grid: Card 1 and Card 2 are in the first row, Card 3 is now centred in the second row. This visual illustrates the state after applying centring to the second row in a responsive layout. Card 1 Card 2 Card 3
@media (max-width: 927px) and (min-width: 632px) {
  .team-wrapper.centred {
    grid-template-columns: repeat(2, minmax(min(100%, 18em), 1fr));
  }
  .team-wrapper.centred .team-card:last-child {
    grid-column: 1 / -1;
    justify-self: center;
    width: 50%;
  }
}
  • @media (max-width: 927px) and (min-width: 632px)
    • Targets screens with a width between 632px and 927px (inclusive).
    • Ensures styles inside only apply to tablets and small desktops, not mobile or large desktop screens.
  • .team-wrapper.centred
    • Applies to elements with both team-wrapper and centred classes.
  • grid-template-columns: repeat(2, minmax(min(100%, 18em), 1fr));
    • Sets up a CSS Grid with 2 columns.
    • Each column’s width is;
    • At least the smaller of 100% (full container width) or 18em (about 288px if base font is 16px).
    • At most 1fr (one fractional unit of available space).
    • This ensures columns are responsive, never exceeding the container or dropping below a readable minimum.
  • .team-wrapper.centred .team-card:last-child
    • Targets the last .team-card inside a centred .team-wrapper.
  • grid-column: 1 / -1;
    • Makes the last card span all grid columns (from first to last).
    • Effectively places it on a row by itself, full width of the grid.
  • justify-self: center;
    • Horizontally centres the last card within its grid area.
  • width: 50%;
    • Restricts the last card’s width to half the grid container, so it doesn’t stretch too wide.
    • Maintains visual balance and focus for the last card.

Adapt to your requirements

To implement responsive centring for the last card in a grid use the browser’s inspector tools to monitor how the grid responds as you resize the viewport.

The device toolbar helps you pinpoint the exact pixel width where the layout shifts from 3 columns to 2 (and 2 to 1.) Once identified, set your media query to match this breakpoint (i.e. change the 632 & 927px values).

Mobile responsive breakpoint

As the viewport shrinks, the grid adapts and expands to 100% width;

Three card layout in a single column (mobile view) A diagram showing three cards stacked vertically in a single column, labelled Card 1, Card 2, and Card 3. This visual represents the mobile layout for a responsive CSS grid. Card 1 Card 2 Card 3

Download sample code

I’ve assembled all the parts so that you can play with them. The standard – software is provided “as is”, without warranty of any kind – applies here.

Zip File Info
/index.html
4.4kB
/style.css
2.1kB
/thispersondoesnotexist-1.jpg
90.3kB
/thispersondoesnotexist-2.jpg
70.2kB
/thispersondoesnotexist-3.jpg
86.3kB
File: barrd-auto-fit-3-cards.zip
Details: 5 files @ 251.7kB

Acknowledgements

The original cool design for the subgrid layout came from Ana Tudor 🙌 and the images of the ‘team’ came from the This Person Does Not Exist website. I created this post and demo to properly wrap my brain around the concepts.

That’s all folks

CSS Grid, especially with subgrid and responsive tricks, lets you build layouts that adapt without endless media queries or hacks. This is a robust, flexible card grid, one that looks sharp from mobile to desktop and keeps your content lined up perfectly.

If you’ve any great examples you’d like to share, reach out and let me know!

// End of Project

More Information

Dave A.K.A. 'barrd'

Dave is a Bristol based Scottish Expat who has 20+ years experience of web development. Loves playing guitar, reading books, watching Sci-Fi and tinkering with tech.

About Dave A.K.A. 'barrd'

Image for Dave A.K.A. 'barrd'
Dave is a Bristol based Scottish Expat who has 20+ years experience of web development. Loves playing guitar, reading books, watching Sci-Fi and tinkering with tech.

Read more about Dave