30 CSS Grid Layouts24 / 30

Pure CSSMIT licensed

Dense Packing

grid-auto-flow: dense, demonstrated honestly: numbered tiles with mixed spans, where the numbers reveal exactly which items the algorithm pulled backward to fill holes. The one grid feature that trades source order for zero gaps — and the demo teaches when that trade is safe.

Published Updated

Live Demo
Try it

The code

<section class="cgl-24" aria-label="Dense packing demo">
  <p class="cgl-24__note">Numbers = DOM order. Spot the small tiles pulled backward to plug holes.</p>
  <div class="cgl-24__grid">
    <div class="cgl-24__cell" style="--s:2">1</div>
    <div class="cgl-24__cell">2</div>
    <div class="cgl-24__cell" style="--s:2;--r:2">3</div>
    <div class="cgl-24__cell">4</div>
    <div class="cgl-24__cell" style="--s:2">5</div>
    <div class="cgl-24__cell">6</div>
    <div class="cgl-24__cell">7</div>
    <div class="cgl-24__cell" style="--s:2">8</div>
    <div class="cgl-24__cell">9</div>
    <div class="cgl-24__cell">10</div>
    <div class="cgl-24__cell" style="--r:2">11</div>
    <div class="cgl-24__cell">12</div>
    <div class="cgl-24__cell">13</div>
    <div class="cgl-24__cell" style="--s:2">14</div>
    <div class="cgl-24__cell">15</div>
  </div>
</section>
.cgl-24,
.cgl-24 *,
.cgl-24 *::before,
.cgl-24 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-24 {
  width: 100%;
  min-height: 100vh;
  --grape: oklch(0.55 0.18 310);
  --bg: oklch(0.17 0.025 310);
  font-family: ui-monospace,'Cascadia Code',Menlo,monospace;
  background: var(--bg);
  padding: 1.4rem;
}

.cgl-24 .cgl-24__note {
  font-size: .72rem;
  color: oklch(0.7 0.06 310);
  margin-block-end: .9rem;
}

.cgl-24 .cgl-24__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(min(76px,100%),1fr));
  grid-auto-rows: 64px;
  grid-auto-flow: row dense;
  gap: 8px;
}

.cgl-24 .cgl-24__cell {
  grid-column: span var(--s,1);
  grid-row: span var(--r,1);
  display: grid;
  place-items: center;
  font-size: 1.05rem;
  font-weight: 700;
  color: oklch(0.9 0.05 310);
  background: color-mix(in oklab,var(--grape) 30%,var(--bg));
  border: 1px solid color-mix(in oklab,var(--grape) 50%,var(--bg));
  border-radius: 10px;
}

.cgl-24 .cgl-24__cell:nth-child(odd) {
  background: color-mix(in oklab,var(--grape) 18%,var(--bg));
}
/* No JavaScript — grid-auto-flow: row dense backfills holes; the numbered tiles expose the reordering. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Grid Layout from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Dense Packing
Source: https://codefronts.com/layouts/css-grid-layouts/dense-packing/

grid-auto-flow: dense, demonstrated honestly: numbered tiles with mixed spans, where the numbers reveal exactly which items the algorithm pulled backward to fill holes. The one grid feature that trades source order for zero gaps — and the demo teaches when that trade is safe.
## HTML
```html
<section class="cgl-24" aria-label="Dense packing demo">
  <p class="cgl-24__note">Numbers = DOM order. Spot the small tiles pulled backward to plug holes.</p>
  <div class="cgl-24__grid">
    <div class="cgl-24__cell" style="--s:2">1</div>
    <div class="cgl-24__cell">2</div>
    <div class="cgl-24__cell" style="--s:2;--r:2">3</div>
    <div class="cgl-24__cell">4</div>
    <div class="cgl-24__cell" style="--s:2">5</div>
    <div class="cgl-24__cell">6</div>
    <div class="cgl-24__cell">7</div>
    <div class="cgl-24__cell" style="--s:2">8</div>
    <div class="cgl-24__cell">9</div>
    <div class="cgl-24__cell">10</div>
    <div class="cgl-24__cell" style="--r:2">11</div>
    <div class="cgl-24__cell">12</div>
    <div class="cgl-24__cell">13</div>
    <div class="cgl-24__cell" style="--s:2">14</div>
    <div class="cgl-24__cell">15</div>
  </div>
</section>
```
## CSS
```css
.cgl-24,
.cgl-24 *,
.cgl-24 *::before,
.cgl-24 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-24 {
  width: 100%;
  min-height: 100vh;
  --grape: oklch(0.55 0.18 310);
  --bg: oklch(0.17 0.025 310);
  font-family: ui-monospace,'Cascadia Code',Menlo,monospace;
  background: var(--bg);
  padding: 1.4rem;
}

.cgl-24 .cgl-24__note {
  font-size: .72rem;
  color: oklch(0.7 0.06 310);
  margin-block-end: .9rem;
}

.cgl-24 .cgl-24__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(min(76px,100%),1fr));
  grid-auto-rows: 64px;
  grid-auto-flow: row dense;
  gap: 8px;
}

.cgl-24 .cgl-24__cell {
  grid-column: span var(--s,1);
  grid-row: span var(--r,1);
  display: grid;
  place-items: center;
  font-size: 1.05rem;
  font-weight: 700;
  color: oklch(0.9 0.05 310);
  background: color-mix(in oklab,var(--grape) 30%,var(--bg));
  border: 1px solid color-mix(in oklab,var(--grape) 50%,var(--bg));
  border-radius: 10px;
}

.cgl-24 .cgl-24__cell:nth-child(odd) {
  background: color-mix(in oklab,var(--grape) 18%,var(--bg));
}
```

## JavaScript
```js
/* No JavaScript — grid-auto-flow: row dense backfills holes; the numbered tiles expose the reordering. */
```

How this works

With default row flow, a span-2 tile that doesn't fit the current row's remainder wraps — leaving a hole. grid-auto-flow: row dense lets the placement algorithm backfill: later SMALL items jump backward into earlier holes. The numbered tiles make the reordering visible — find tile 7 sitting before tile 5 and you've seen the algorithm work.

The demo's rule of thumb, encoded in its caption: dense is for BROWSING surfaces (galleries, tag clouds, moodboards) where visual completeness beats sequence. It's wrong for ranked or chronological content — and note that keyboard tab order still follows the DOM, so interactive dense grids create focus order that hops around the page (a WCAG 2.4.3 concern).

Make it yours

  • Toggle dense off (grid-auto-flow: row) to compare — the demo's holes reappear instantly.
  • Dense + auto-fit + minmax composes; the backfill works at every column count.
  • Constrain chaos: give only 1×1 items freedom to backfill by keeping all spans ≤2.
  • For ranked content needing gap-free looks, redesign spans (demo 23's arithmetic) instead of reordering.

Gotchas — read before shipping

  • Never use dense on content where reading order carries meaning — search results, feeds, steps.
  • Keyboard focus follows DOM order, not visual order; avoid dense for grids of links/buttons unless order genuinely doesn't matter.
  • Dense can't fix holes caused by spans WIDER than remaining tracks at the grid's end — those need span discipline.

Browser support

ChromeSafariFirefoxEdge
111+16.2+113+111+

Floor set by oklch(), color-mix() as this demo is written. The core technique itself goes back further — Chrome 57+.

dense shipped with original grid — universal since 2017. The lesson, not the support, is the constraint.

Techniques used in this demo

Search CodeFronts

Loading…