Grid lays out children across both rows and columns simultaneously, defined explicitly by the container rather than inferred item-by-item.
.page {
display: grid;
grid-template-columns: 250px 1fr; /* fixed sidebar, flexible main content */
grid-template-rows: auto 1fr auto; /* header, content, footer */
gap: 16px;
}
fr is a Grid-only unit meaning "a fraction of the remaining space after fixed-size tracks are accounted for" — 1fr alongside a fixed 250px column means that column gets everything left over.
Named grid areas make a layout genuinely readable from the CSS alone:
.page {
display: grid;
grid-template-columns: 250px 1fr;
grid-template-areas:
"sidebar header"
"sidebar content"
"sidebar footer";
}
.sidebar { grid-area: sidebar; }
.header { grid-area: header; }
.content { grid-area: content; }
.footer { grid-area: footer; }
The grid-template-areas string literally draws the layout as ASCII art — each row of the string is a row of the grid, and repeating a name (like sidebar across three rows) makes that item span all of them.
repeat() and auto-fit/auto-fill handle responsive card grids without a single media query:
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
This creates as many 200px-minimum columns as fit in the container, stretching them evenly to fill any remaining space — the number of columns adjusts automatically as the container resizes, with zero JavaScript and zero breakpoints.