CSS Grid: rows and columns in one step
A gallery of cards isn't a single row. It's rows and columns, with every card the same width. Grid handles both directions with a couple of classes.
The big idea
A grid container divides its space into columns and places its children into the resulting cells, row by row.
See it in code
grid makes the container a grid. grid-cols-3 asks for three equal columns, and gap-4 spaces the cells in both directions:
<div class="grid grid-cols-3 gap-4 p-6">
<div class="rounded-xl bg-white/5 p-6">Perseverance</div>
<div class="rounded-xl bg-white/5 p-6">Webb</div>
<div class="rounded-xl bg-white/5 p-6">Juno</div>
</div>You never set a card's width. The grid divides the available space into three equal tracks, and each child takes one.
Add a fourth child and it wraps to a new row by itself. You describe the columns; the grid creates as many rows as it needs:
<div class="grid grid-cols-3 gap-4 p-6">
<div class="rounded-xl bg-white/5 p-6">Perseverance</div>
<div class="rounded-xl bg-white/5 p-6">Webb</div>
<div class="rounded-xl bg-white/5 p-6">Juno</div>
<div class="rounded-xl bg-white/5 p-6">Cassini</div>
</div>The fourth card is exactly as wide as the others, lined up under the first. Adding content never requires rethinking the layout.
col-span-2 makes one child take two columns. The other children flow around it automatically:
<div class="grid grid-cols-3 gap-4 p-6">
<article class="col-span-2 rounded-xl border border-violet-400/40 bg-violet-500/10 p-6">
<p class="text-xs uppercase tracking-widest text-violet-300">Featured</p>
<h3 class="mt-1 text-xl font-bold">Juno</h3>
<p class="mt-1 text-sm text-slate-300">Orbiting Jupiter since 2016 and mapping its storms.</p>
</article>
<article class="rounded-xl bg-white/5 p-6"><h3 class="font-bold">Webb</h3></article>
<article class="rounded-xl bg-white/5 p-6"><h3 class="font-bold">Cassini</h3></article>
<article class="rounded-xl bg-white/5 p-6"><h3 class="font-bold">Apollo 8</h3></article>
</div>The featured card and one normal card fill the three columns of row one. The remaining two start row two, leaving one cell empty.
Two-dimensional layout is the grid's whole purpose: photo galleries, dashboards, calendars, pricing tables, the tiles on a phone's home screen.
Try it yourself
Move col-span-2 from Juno to the last card and predict where the empty cell ends up. Then change grid-cols-3 to grid-cols-2 and re-read the layout.
The common mistake
A typo in the columns class. grid-col-3, without the s, is ignored, and a grid with no column definition is a single column: every card stacks full width.
What it unlocks
Grid and flexbox are partners: grid for the page's cells, flex inside each one. To change the number of columns by screen size, add responsive prefixes.