Four Layers of Modern CSS Layout: From Flexbox to Container Queries
A clear map: Flexbox solves one dimension, Grid solves two, Subgrid pierces nesting, Container Queries respond to the container. A single card component walks through each technique's proper boundary.
Summary: Choosing a layout approach isn’t “newer is better” — it’s “use the right layer”. This post walks a blog card component through four modern CSS layout techniques and their trade-offs.
The overview
| Technique | Dimensions | Solves | Browser support |
|---|---|---|---|
| Flexbox | One | Main-axis alignment, even distribution | 2015+ |
| Grid | Two | Rows and columns together | 2017+ |
| Subgrid | Nested grids | Alignment across layers | 2023+ |
| Container Queries | Container scope | Component-level responsiveness | 2023+ |
Layer 1: Flexbox, the one-dimensional Swiss army knife
Flexbox handles “how to arrange things along one line (or column)”. The classic cases are nav bars and card action areas:
.card__actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.card__actions .spacer {
flex: 1; /* pushes later buttons to the right */
}
Three rules of thumb: gap replaces margins, flex: 1 controls growth/shrink, and min-width: 0 unlocks text truncation.
Layer 2: Grid, real two-dimensional control
When a card list needs rows and columns constrained at once, Grid is the answer:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
gap: 1.5rem;
}
auto-fit + minmax() is the golden combo for responsive grids: the column count derives from container width, no media queries needed.
Layer 3: Subgrid, aligning through nesting
Nested grids share one pain: the inner grid can’t align with the outer tracks. Subgrid fixes it — the inner grid reuses the parent’s tracks directly:
.article-list {
display: grid;
grid-template-columns: 1fr 2fr; /* outer tracks */
}
.article-list > .item {
display: grid;
grid-template-rows: subgrid; /* rows pierce to the parent */
grid-row: span 3;
}
Now a set of cards aligns date, title, and excerpt row-by-row no matter how long the content is — pure CSS, zero JS measuring.
Layer 4: Container Queries, the component’s own responsiveness
Media queries respond to the viewport; container queries respond to the container. The same component in a sidebar (narrow) and in the main column (wide) should render differently:
.card {
container-type: inline-size;
}
@container (min-width: 24rem) {
.card__body {
display: grid;
grid-template-columns: 8rem 1fr;
}
}
Combined with container-name, you can also target a specific named container — building genuinely reusable “smart components”.
A decision tree
- Arranging one line/column? → Flexbox
- Rows and columns at once? → Grid
- Aligning through nested grids? → Subgrid
- Component varies with its host container? → Container Queries
- None of the above? → Check whether you need CSS at all; sometimes
margin: autois enough
Wrap-up
Modern CSS layout isn’t a zero-sum game of “Grid replaces Flexbox” — it’s four layers stacking on top of each other. Use the right layer first, then optimize; that stabilizes code quality far better than chasing new features.