30 CSS Grid Layouts12 / 30

Pure CSSMIT licensed

Quantity Query

A grid that restyles itself based on HOW MANY children it has — pure CSS, via :has() and child-counting selectors. Three tags render as roomy feature cards; six or more automatically compact into a dense chip wall. The CMS-proof layout: editors add items, the design responds, nobody files a ticket.

Published Updated

Live Demo
Try it

The code

<section class="cgl-12" aria-label="Quantity query demo">
  <p class="cgl-12__label">3 items → feature cards</p>
  <ul class="cgl-12__grid">
    <li><strong>Design</strong><span>Roomy, confident, editorial.</span></li>
    <li><strong>Engineering</strong><span>Same class, same stylesheet.</span></li>
    <li><strong>Research</strong><span>No modifiers, no JS.</span></li>
  </ul>
  <p class="cgl-12__label">8 items → the grid self-compacts via :has(> :nth-child(5))</p>
  <ul class="cgl-12__grid">
    <li><strong>Design</strong></li><li><strong>Engineering</strong></li><li><strong>Research</strong></li><li><strong>Ops</strong></li>
    <li><strong>Data</strong></li><li><strong>Support</strong></li><li><strong>Legal</strong></li><li><strong>Growth</strong></li>
  </ul>
</section>
.cgl-12,
.cgl-12 *,
.cgl-12 *::before,
.cgl-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-12 {
  width: 100%;
  min-height: 100vh;
  --amber: oklch(0.78 0.15 75);
  --coal: oklch(0.2 0.01 75);
  --card: oklch(0.25 0.015 75);
  font-family: system-ui,sans-serif;
  background: var(--coal);
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: .8rem;
}

.cgl-12 .cgl-12__label {
  font-family: ui-monospace,Menlo,monospace;
  font-size: .68rem;
  color: color-mix(in oklab,var(--amber) 70%,white);
  letter-spacing: .08em;
}

.cgl-12 .cgl-12__grid {
  --floor: 200px;
  --pad: 1.1rem;
  --fs: .95rem;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(min(var(--floor),100%),1fr));
  gap: 12px;
  margin-block-end: .6rem;
}

.cgl-12 .cgl-12__grid:has(>:nth-child(5)) {
  --floor: 104px;
  --pad: .6rem;
  --fs: .78rem;
  gap: 8px;
}

.cgl-12 .cgl-12__grid li {
  background: var(--card);
  border: 1px solid color-mix(in oklab,var(--amber) 25%,var(--card));
  border-radius: 12px;
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  gap: .4rem;
}

.cgl-12 .cgl-12__grid li strong {
  font-size: var(--fs);
  color: var(--amber);
}

.cgl-12 .cgl-12__grid li span {
  font-size: .78rem;
  line-height: 1.5;
  color: oklch(0.75 0.02 75);
}

.cgl-12 .cgl-12__grid:has(>:nth-child(5)) li {
  align-items: center;
  text-align: center;
  justify-content: center;
}
/* Legacy (pre-:has) equivalent: li:first-child:nth-last-child(n+5), li:first-child:nth-last-child(n+5) ~ li { ... } */
/* No JavaScript — .grid:has(> :nth-child(5)) detects the item count and flips custom properties for the compact state. */
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: Quantity Query
Source: https://codefronts.com/layouts/css-grid-layouts/quantity-query/

A grid that restyles itself based on HOW MANY children it has — pure CSS, via :has() and child-counting selectors. Three tags render as roomy feature cards; six or more automatically compact into a dense chip wall. The CMS-proof layout: editors add items, the design responds, nobody files a ticket.
## HTML
```html
<section class="cgl-12" aria-label="Quantity query demo">
  <p class="cgl-12__label">3 items → feature cards</p>
  <ul class="cgl-12__grid">
    <li><strong>Design</strong><span>Roomy, confident, editorial.</span></li>
    <li><strong>Engineering</strong><span>Same class, same stylesheet.</span></li>
    <li><strong>Research</strong><span>No modifiers, no JS.</span></li>
  </ul>
  <p class="cgl-12__label">8 items → the grid self-compacts via :has(> :nth-child(5))</p>
  <ul class="cgl-12__grid">
    <li><strong>Design</strong></li><li><strong>Engineering</strong></li><li><strong>Research</strong></li><li><strong>Ops</strong></li>
    <li><strong>Data</strong></li><li><strong>Support</strong></li><li><strong>Legal</strong></li><li><strong>Growth</strong></li>
  </ul>
</section>
```
## CSS
```css
.cgl-12,
.cgl-12 *,
.cgl-12 *::before,
.cgl-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cgl-12 {
  width: 100%;
  min-height: 100vh;
  --amber: oklch(0.78 0.15 75);
  --coal: oklch(0.2 0.01 75);
  --card: oklch(0.25 0.015 75);
  font-family: system-ui,sans-serif;
  background: var(--coal);
  padding: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: .8rem;
}

.cgl-12 .cgl-12__label {
  font-family: ui-monospace,Menlo,monospace;
  font-size: .68rem;
  color: color-mix(in oklab,var(--amber) 70%,white);
  letter-spacing: .08em;
}

.cgl-12 .cgl-12__grid {
  --floor: 200px;
  --pad: 1.1rem;
  --fs: .95rem;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(min(var(--floor),100%),1fr));
  gap: 12px;
  margin-block-end: .6rem;
}

.cgl-12 .cgl-12__grid:has(>:nth-child(5)) {
  --floor: 104px;
  --pad: .6rem;
  --fs: .78rem;
  gap: 8px;
}

.cgl-12 .cgl-12__grid li {
  background: var(--card);
  border: 1px solid color-mix(in oklab,var(--amber) 25%,var(--card));
  border-radius: 12px;
  padding: var(--pad);
  display: flex;
  flex-direction: column;
  gap: .4rem;
}

.cgl-12 .cgl-12__grid li strong {
  font-size: var(--fs);
  color: var(--amber);
}

.cgl-12 .cgl-12__grid li span {
  font-size: .78rem;
  line-height: 1.5;
  color: oklch(0.75 0.02 75);
}

.cgl-12 .cgl-12__grid:has(>:nth-child(5)) li {
  align-items: center;
  text-align: center;
  justify-content: center;
}
/* Legacy (pre-:has) equivalent: li:first-child:nth-last-child(n+5), li:first-child:nth-last-child(n+5) ~ li { ... } */
```

## JavaScript
```js
/* No JavaScript — .grid:has(> :nth-child(5)) detects the item count and flips custom properties for the compact state. */
```

How this works

The counting trick is .grid:has(> :nth-child(5)) — true only when a 5th child exists. That one selector gates a whole restyle: smaller minmax floor, tighter gap, compact typography. Before :has(), this needed JavaScript or the arcane li:first-child:nth-last-child(n+5) sibling chain (shown in the comment for legacy targets).

Both demo grids share ONE class and ONE stylesheet — the top grid has 3 items and renders as feature cards; the bottom has 8 and self-compacts. The quantity threshold, spacing and type scale all live in custom properties flipped by the :has() rule, so the two "designs" are one design with a counted state.

Make it yours

  • Move the threshold by changing the nth-child index (5 → any count).
  • Add a middle tier: chain two :has() gates (n+4 roomy, n+7 compact) for three density states.
  • Flip more than layout — the pattern gates colour, radius, even content-visibility.
  • Use the legacy sibling-count chain inside the :has() block if you must support 2022 browsers.

Gotchas — read before shipping

  • :has() invalidates ancestor styles on DOM changes — cheap here, but don't hang thousand-node trees off quantity gates.
  • Count DIRECT children only (the > combinator) — nested markup inflates counts silently.
  • Design both states deliberately; auto-compacting into an unconsidered layout is worse than not compacting.

Browser support

ChromeSafariFirefoxEdge
111+16.2+121+111+

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

:has() is the gate — universal in evergreens since Dec 2023. The sibling-chain fallback works back to IE9 if you ever need it.

Techniques used in this demo

Search CodeFronts

Loading…