CSS & layout

Container queries observe the component, not the viewport

Container queries observe the component, not the viewport

Drop a card component into a two-column layout. In the wide column it lays out as a row: image left, copy right. Move the same card into a 320px sidebar and it still tries to be a row, because its breakpoint was written against the viewport, not the box it now lives in. That is the pattern Victor Ayomipo, writing in Smashing Magazine on 16 September, is trying to get you to stop shipping.

Where each query lives

Media queries answer the browser. Container queries answer the box.

Ayomipo frames it as "macro" vs "micro" layout. A media query asks the viewport how wide the page is. A container query asks a specific ancestor element how wide it is. Reuse the same breakpoints across both and the component reads the wrong signal.

The State of CSS numbers he cites explain why the mismatch keeps happening: 86% of developers know container queries exist, and 41.4% actually use them. Browser support sits at roughly 94% (caniuse.com), so the gap is not the engines. It's the habit of copying media-query shapes into new syntax.

The two-line wiring

The minimum shown in the source to make a card respond to its own width:

.card-wrapper {
  container-name: card;
  container-type: inline-size;
}

@container card (min-width: 450px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

container-type: inline-size gives the wrapper a size a descendant can query. container-name labels it. Then @container card (...) reads that wrapper's inline size, not the viewport. Drop the card in a 300px rail: the row layout stays off. Drop it in a 600px column: it flips. Same component, two contexts.

One rule to internalise, per the article: you cannot query the element you are styling. The component and its container have to be different nodes, or the layout goes into a loop. That is the reason for the wrapper.

Container units, same idea

Container units are the other half. Ayomipo's typography example:

.card-title {
  font-size: clamp(1rem, .5rem + 3cqi, 2rem);
}

cqi is 1% of the container's inline size. Wired to a container-type: inline-size ancestor, clamp() gives the title a floor, a ceiling, and a smooth ramp between them driven by the box rather than the viewport. The card grows and shrinks with its column. No media-query stack, no per-breakpoint font sizes.

The rough edges

The article walks through a flex-wrap detection pattern for a card grid: flex items sized flex: 1 1 390px are each given container-type: inline-size, then an @container (min-width: 600px) rule flips a card from column to row once it stops sharing its row with siblings. The card notices when it is alone. The viewport is not involved.

Ayomipo also flags the sharp corners. Querying block size can collapse a layout when the container has no explicit height. Custom-property values do not work inside a container-query condition, so a shared --breakpoint variable is out. And the wrapper element is real DOM; a component still cannot query itself.

What to try next

Pick one component on your current project that reads viewport width — a card, a media object, a summary tile. Wrap it, set container-type: inline-size on the wrapper, and rewrite its breakpoints as @container rules with cqi for the type ramp. Drop it in three contexts on the same page. If it renders the same layout in a 300px rail and a 900px column, the breakpoints are still viewport-shaped. Kevin Powell, quoted from a SmashingConf Amsterdam 2026 talk, called container-query adoption "terrible" for exactly that reason.

Source: Smashing Magazine (smashingmagazine.com)

Related
CSS & layout

Your icon is centered against the wrong thing

Ahmad Shadeed's new piece nails the icon-and-label bug that has been quietly wrong on every design system list for years, and the fix is one transform in the lh unit.

September 16, 2026
CSS & layout

flex-wrap: balance ends the wrapped-flex orphan

Chrome 150 ships flex-wrap: balance, a new value that distributes wrapped flex items so a single trailing item stops landing alone on its own line. Ahmad Shadeed walks through the setup, the workarounds it retires, and a companion flex-line-count knob.

September 7, 2026
CSS & layout

One grid, five rounds, no track matching

A rebuild of the FIFA knockout layout with CSS subgrid shows how nested rounds can share the parent grid's columns and rows, with negative line numbers doing the trimming.

July 19, 2026