CSS grid-auto-flow: Dense Packing Explained
grid-auto-flow: dense reorders grid items to fill leftover gaps automatically — how it works, when to use it, and the accessibility tradeoff it hides.
The grid-auto-flow property controls how CSS Grid places items that don’t have explicit row and column positions, and its dense keyword tells the grid to backfill gaps left by earlier items instead of leaving them empty. It’s one of the few CSS properties that trades visual tidiness for a change in DOM-to-screen ordering, which is exactly why it needs to be used carefully.
What grid-auto-flow actually does
By default, grid-auto-flow is row: the browser places items one row at a time, in source order, wrapping to a new row when the current one runs out of space. Switch it to column and the same logic runs along columns instead. Both variants are “sparse” — once the algorithm passes a gap, it never goes back to fill it, even if a later item would fit there.
Add dense (grid-auto-flow: row dense or column dense) and the algorithm changes: for every new item, it first scans backward for any earlier gap the item can fit into, and places it there if possible. This matters most with a mix of item sizes — a masonry-like grid of cards where some span two columns and others span one, for instance, benefits from dense because it closes the holes that odd-sized spans leave behind. This builds on the placement model covered in CSS grid vs flexbox and pairs naturally with repeat() and minmax() for responsive track sizing.
A concrete example
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-flow: row dense;
gap: 1rem;
}
.gallery .featured {
grid-column: span 2;
grid-row: span 2;
}
Without dense, a featured item that doesn’t fit flush against the current row edge pushes every subsequent item down and to the right of it, leaving triangular gaps in a four-column layout. With dense, the browser fills those gaps with whichever later items fit, producing a tighter, more magazine-like grid.
The catch: visual order versus source order
dense only changes where items are painted, not the order they appear in the DOM or the accessibility tree. A screen reader, a “tab through the page” keyboard user, and a search engine crawler all still encounter items in source order — but a sighted mouse user sees them rearranged. If your grid mixes clickable cards (products, articles, search results), this mismatch can put an item that appears third on screen fourth or fifth in the actual tab sequence, which is a genuine usability regression for keyboard and assistive-technology users. The CSS specification itself flags this as a known tradeoff, not a bug to be worked around — it’s inherent to how dense is defined.
The safe uses of dense are grids where item order doesn’t carry meaning: photo galleries, dashboard tiles, or decorative layouts where any item could reasonably appear anywhere. Avoid it for sequential content like a numbered list, a timeline, or paginated search results where users reasonably expect what they read to match what they tab through.
grid-auto-flow versus explicit placement
You don’t need grid-auto-flow at all if you’re willing to place every item explicitly with grid-column and grid-row — but that doesn’t scale to dynamic content where the number of items isn’t known ahead of time. grid-auto-flow (with or without dense) is the tool for exactly that case: a grid whose item count varies, sized automatically by grid-template-areas for the parts you do want to control explicitly, and auto-flow for the rest. For layouts that need to adapt to the container itself rather than the viewport, pair this with container queries; for grids nested inside other grids, subgrid lets inner tracks align to the parent’s rather than defining their own.
When to reach for dense
- Photo or card galleries with mixed item sizes — the classic case, and where the gap-filling benefit is largest.
- Dashboards where tile order is cosmetic, not informational.
- Masonry-style layouts before native masonry support is available everywhere —
denseis the closest CSS-only approximation today.
Skip it for anything where reading order matters: articles, search results, forms, or any list a user might reasonably want to navigate top-to-bottom by keyboard.
The takeaway
grid-auto-flow: dense closes the gaps that mixed-size grid items leave behind, by reordering where items are painted while leaving their source order untouched. That’s a genuine layout win for galleries and dashboards, but it silently decouples visual order from tab order and screen-reader order — so reserve it for grids where item order was never meaningful in the first place, and leave sequential content on the default sparse algorithm.
Tagged
Keep reading
Takina · · 4 min read What Is CSS Subgrid? Nested Grid Alignment Explained
CSS subgrid lets a nested grid item inherit its parent's track sizing, so children can align to the same columns or rows across unrelated containers.
Takina · · 4 min read CSS field-sizing Property Explained
The CSS field-sizing property lets form controls like textareas grow to fit their content automatically, without JavaScript resize listeners.
Takina · · 3 min read CSS Scrollbar Styling: scrollbar-color and scrollbar-width
CSS now styles scrollbars natively with scrollbar-color and scrollbar-width, replacing years of vendor-prefixed hacks. Here's how they work.