Card grids with uneven heights that still keep their inner elements aligned
Masonry layout packs cards of different heights tightly together, subgrid precisely aligns inner elements to the track grid of a parent grid. Combined, the two techniques solve a problem that plain flexbox or a simple grid alone cannot, but only with a clear understanding of what masonry actually supports today and what it does not.
Table of Contents
- 1. The problem: uneven card heights that still need aligned content
- 2. Subgrid in brief: tying nested grids to the parent's track grid
- 3. Masonry layout in brief: grid-template-rows: masonry
- 4. The key insight: masonry only affects one axis, subgrid can use the other
- 5. Concrete example: a featured card with subgrid columns inside a masonry grid
- 6. A full card grid with regular and subgrid cards
- 7. Browser support reality: why the full combination rarely works today
- 8. Fallback strategy: a regular grid without masonry as a solid baseline
- 9. Practical recommendation: when the combination already pays off today
- 10. Summary
- 11. FAQ
1. The problem: uneven card heights that still need aligned content
A card grid of article previews with varying text length produces either large gaps with classic CSS grid, when every row stretches to the tallest card, or a messy picture when cards flow freely. A Pinterest-style masonry layout packs each card into the currently shortest column, avoiding unnecessary gaps without artificially forcing every card to the same height.
At the same time, a wider, featured card spanning two columns should align its own inner elements, such as title, meta line and call-to-action button, to exactly the same column lines the rest of the single-column cards use. That is exactly what subgrid is for: it ties a nested grid structure to the track grid of the outer grid, instead of opening its own, independent coordinate system.
2. Subgrid in brief: tying nested grids to the parent's track grid
Normally every nested display: grid element opens its own, independent coordinate system of columns and rows, unrelated to the track grid of the surrounding grid. The subgrid value for grid-template-columns or grid-template-rows fundamentally changes that: a grid item that itself spans several tracks of the parent grid can adopt those tracks directly, instead of defining its own, potentially different track sizes.
The result is precise alignment across component boundaries, something that previously could only be approximated with manually synchronized, hardcoded width values or CSS custom properties. For cards, forms, or table rows that need to align internally with the same grid as their surroundings, subgrid is now broadly supported and production-ready.
3. Masonry layout in brief: grid-template-rows: masonry
The experimental masonry layout is activated with grid-template-rows: masonry and tells the browser to no longer compute the row axis of a grid using the classic grid algorithm with equal-height row tracks, but instead to place every item into whichever column currently has the least occupied space, similar to how JavaScript libraries have manually implemented Pinterest-style layouts for years.
An often-overlooked property of the specification matters here: masonry only changes the axis it is applied to, in this case rows. The column axis remains a completely regular, explicit track grid, defined as usual through grid-template-columns. This exact property is the key that makes combining masonry and subgrid meaningful at all.
4. The key insight: masonry only affects one axis, subgrid can use the other
Because a masonry grid with grid-template-rows: masonry keeps its column axis unchanged as a regular, explicit track grid, a child element spanning several of those columns can set its own grid-template-columns: subgrid and inherit the column lines directly from the parent grid. The row axis, by contrast, stays unsuitable for subgrid, because masonry does not generate fixed, addressable tracks there, but applies a dynamic packing logic instead.
In practice this means subgrid works within a masonry context exclusively for column alignment of elements that span multiple columns, never for row alignment. This limitation is not a bug but follows directly from how masonry works, and should be factored into the layout design from the start rather than surfacing later as a surprise.
5. Concrete example: a featured card with subgrid columns inside a masonry grid
The following example shows a four-column masonry grid where a featured card spans two columns. This card uses grid-template-columns: subgrid to align its internal breakdown of image, title and call-to-action exactly to the two spanned columns of the outer grid, instead of computing its own, independent two-column split.
Because the outer grid's row axis is dynamically packed by masonry, the card gets its own height internally through a regular grid-template-rows, independent of the parent grid. Only the column alignment is inherited, the height stays a component's own concern and adapts to the actual card content.
.card-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: masonry;
gap: 1.5rem;
}
.card--featured {
grid-column: span 2;
display: grid;
grid-template-columns: subgrid; /* inherits the parent grid's 2 tracks */
grid-template-rows: auto 1fr auto; /* own rows, independent of the masonry grid */
gap: 1rem;
}
.card--featured .card-title {
grid-column: 1 / -1;
}
.card--featured .card-meta {
grid-column: 1;
}
.card--featured .card-cta {
grid-column: 2;
justify-self: end;
}
6. A full card grid with regular and subgrid cards
In a real project, most cards in a grid are simple, single-column elements that do not need subgrid, because they only ever occupy a single column track anyway. Subgrid is worth applying specifically to the minority of multi-column, featured cards, while the rest of the grid gets by entirely without any nested grid declaration.
This mixed architecture, a masonry grid as the base with a handful of subgrid cards for special cases, keeps the code manageable and ensures subgrid is only used where it actually adds value, namely for elements that span multiple tracks of the parent grid.
.card {
/* regular card: no subgrid declaration needed */
grid-column: span 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.card-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: masonry;
gap: 1.5rem;
align-tracks: start; /* prevents vertical stretching within a masonry track */
}
7. Browser support reality: why the full combination rarely works today
Subgrid itself is now broadly available across all current versions of Chrome, Firefox and Safari and counts as production-ready. Masonry, by contrast, is far less mature: grid-template-rows: masonry is so far primarily implemented experimentally in Firefox, while other browser engines pursue an alternative approach called item-flow layout, which is neither syntactically nor conceptually identical.
This divergence means a true, spec-compliant combination of masonry and subgrid currently only realistically works in full in a single browser, while it gets either completely ignored or meets a different mechanism in every other. For production projects, that is a decisive difference from subgrid alone, which has long been broadly usable.
8. Fallback strategy: a regular grid without masonry as a solid baseline
The most reliable approach is to build the layout fundamentally with a regular, non-experimental grid where every card in the same row shares the same height, and to add masonry purely as a progressive enhancement behind a feature query. Subgrid keeps working normally within this fallback, independent of the masonry fallback, because it is not tied to the row axis, only to column alignment.
This strategy delivers a functional, if not perfectly space-efficient, result in every browser, while users of a browser with masonry support automatically get the more tightly packed, visually more dynamic layout. It matters not to hardcode heights in the fallback branch, but to keep working with auto tracks, so the transition between the two variants does not feel visually abrupt.
.card-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: auto; /* solid baseline: cards in the same row share the height */
gap: 1.5rem;
}
@supports (grid-template-rows: masonry) {
.card-grid {
grid-template-rows: masonry;
}
}
9. Practical recommendation: when the combination already pays off today
For internal tools, prototypes, or projects with an audience clearly restricted to a single browser, for example an internal Firefox-based dashboard, the full combination of masonry and subgrid can already be used in production today. For publicly accessible websites with a mixed browser audience, the combination of a solid grid fallback with progressive masonry enhancement remains the more pragmatic route.
Subgrid for aligning the columns of featured cards can be used confidently in production right now regardless of whether masonry is even in play, because subgrid itself has long enjoyed broad support. Anyone looking to replace JavaScript masonry libraries with native solutions today should therefore consistently hide native masonry behind a feature query rather than shipping it as the only solution.
| Layout strategy | Packs uneven heights | Inner alignment across cards | Browser support |
|---|---|---|---|
grid-template-rows: masonry + subgrid |
Yes, tightly packed | Yes, through subgrid columns | Experimental only, mainly Firefox |
| Regular grid with equal row height | No, gaps from the tallest card | Yes, through regular tracks | Full, all current browsers |
CSS multi-column (columns) |
Yes, similar masonry look | No, no row alignment possible | Full, all current browsers |
Flexbox with flex-wrap |
No, row height follows the tallest card | Partial, without a real track grid | Full, all current browsers |
| JavaScript masonry library | Yes, tightly packed | Yes, with manual extra logic | Full, but an extra JS bundle |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
Combining Subgrid and Masonry: The Essentials at a Glance
Core idea
Masonry only changes one axis (usually rows), the other axis stays a regular track grid that subgrid can still use.
Subgrid's role
grid-template-columns: subgrid aligns multi-column, featured cards exactly to the columns of the masonry grid.
Support reality
Subgrid is broadly supported, masonry is currently mainly experimental in Firefox, other engines pursue a different approach.
Fallback strategy
Regular grid as the baseline, masonry added as a progressive enhancement through @supports(grid-template-rows: masonry).