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.
The core grid setup
.team-wrapper {
grid-template:
repeat(3, auto) /
repeat(auto-fit, minmax(min(100%, 18em), 1fr));
}
display: grid;turns.team-wrapperinto a grid container.grid-template:is shorthand forgrid-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-fitfills 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.
.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-cardthat 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.
- Selects all direct children of
.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.
- Selects all
.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.
- Applies
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.
If the automated flow, with the third element wrapping to the left just won’t do then…
Here’s the fix;
@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-wrapperandcentredclasses.
- Applies to elements with both
grid-template-columns: repeat(2, minmax(min(100%, 18em), 1fr));- Sets up a
CSSGrid with 2 columns. - Each column’s width is;
- At least the smaller of
100%(full container width) or18em(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.
- Sets up a
.team-wrapper.centred .team-card:last-child- Targets the last
.team-cardinside a centred.team-wrapper.
- Targets the last
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;
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.
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
Further Reading
- CSS Grid MDN Docs (developer.mozilla.org)
- CSS Subgrid MDN Docs (developer.mozilla.org)
- Original Code Example (codepen.io)
- This Person Does Not Exist (thispersondoesnotexist.com)