CSS grid wrap is not a property. Grid items move to the next row when the track definition produces fewer columns than there are items.
The rule that does the work is repeat(auto-fit, minmax(...)) in grid-template-columns. Nothing else is needed for a responsive card layout.
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
}

Read it right to left. Each track is at least 220px and at most one equal share. The browser fits as many as it can, and the rest go to the next row.
Why there is no wrap property
Flex lays items along a single line, so flex-wrap: wrap gives permission to start another. Grid starts from a defined structure of rows and columns.
An item that does not fit the defined columns creates an implicit row instead. The wrapping is already there; what you control is how many columns exist.
| Question | Flex | Grid |
|---|---|---|
| How do items move to a new line | flex-wrap: wrap |
More items than columns |
| How many per line | Whatever fits | The track definition |
| Columns line up between rows | No | Yes |
| Last row alignment | Often ragged | Follows the same tracks |
The third row is why grid is the better choice for card layouts. In a wrapped flex container the second row rarely aligns with the first.
auto-fit against auto-fill
Both keywords create as many tracks as the container allows. They differ only when there are fewer items than tracks.
/* three items in a container wide enough for five */
.a { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
.b { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
- auto-fill keeps the two empty tracks. The three items stay 200px wide, and empty space sits at the end of the row.
- auto-fit collapses the empty tracks to zero width. The three items stretch across the whole row.

Pick auto-fit when items should always fill the row. Pick auto-fill when a consistent card width matters more than a flush edge, for example an image gallery.
Stopping overflow
Three things cause a grid to overflow instead of wrapping.
- The minimum is larger than the container.
minmax(320px, 1fr)in a 300px column cannot shrink. Writeminmax(min(320px, 100%), 1fr)so the minimum yields on narrow screens. - An item refuses to shrink. Grid items default to
min-width: auto, which respects content. A long URL or a wide table forces the track open. Setmin-width: 0on the item. - Padding counted on top of the width. Add
box-sizing: border-box, described in box sizing.
.cards { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.cards > * { min-width: 0; }
Those two lines resolve the large majority of reported wrapping failures.
Controlling the last row
A row with fewer items than tracks stretches under auto-fit. If that looks wrong, two options exist.
/* keep natural width, pack to the start */
.cards {
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
justify-content: start;
}
Or cap the individual item width with max-width so a lone card on the final row does not span the container.
Rows created by wrapping
Tracks the browser adds because items ran out of columns are implicit rows. They size to their content by default, which means a row with one tall card makes every card in that row tall.
grid-auto-rows sets their height explicitly.
.cards {
grid-auto-rows: minmax(160px, auto);
}
That reads as a minimum of 160px, growing if the content needs more. It stops a row of short cards from collapsing while still letting a long one expand.
To stop the stretching entirely, align the items instead of the tracks.
.cards { align-items: start; }
Now each card is as tall as its own content and the row is as tall as the tallest. Which you want depends on whether the cards read as a set or as individual items.
Gap counts against the available width
A common miscalculation: with a 16px gap and a 240px minimum, four columns need 960px of tracks plus 48px of gaps, so 1008px. The browser accounts for this automatically, but it explains why a container you expected to hold four columns holds three.
| Container width | Min 240px, gap 16px | Columns |
|---|---|---|
| 700px | 240 + 16 + 240 = 496 fits, third needs 752 | 2 |
| 800px | Third column needs 752 | 3 |
| 1100px | Fourth needs 1008 | 4 |
| 1300px | Fifth needs 1264 | 5 |
If the break points land in the wrong place, adjust the minimum rather than adding media queries. Every 20px of minimum moves the whole ladder.
Wrapping text inside the cells
A grid that wraps correctly can still overflow because of what is inside it. Long unbroken strings, a URL or a serial number, force a track wider than its stated maximum.
.cards > * {
min-width: 0;
overflow-wrap: anywhere;
}
min-width: 0 overrides the automatic minimum that respects content. overflow-wrap: anywhere lets the string break mid word rather than pushing the track open.
Tables inside grid cells need the same treatment plus table-layout: fixed, otherwise the table's own sizing algorithm wins over the track maximum.
Wrapping in the other direction
Items can also be told to span more than one track, which changes where wrapping occurs.
.feature { grid-column: span 2; }
If the container is only one track wide, span 2 would overflow. Guard it with grid-column: span 2 inside a media query, or use min(2, ...) style logic by scoping the rule.
Checking the layout at several widths

Resize the window and watch where the break points land. The useful widths to check are roughly 380px, 700px and 1200px.
Open the file in the HTML file opener to confirm the layout holds outside your editor, then paste it into a document and share it as a link. A reader on a phone sees the wrapped version without you exporting anything.
For the spacing between wrapped rows see CSS grid row gap. For the wider property set see CSS grid, and flexbox for the cases where a single line is what you actually want.