Web development

Container queries vs media queries: who decides the layout

Media queries watch the screen, container queries watch the component's space. That decides whether a card adapts anywhere or only at one breakpoint.

Key points

  • Media query: a reaction to the viewport. Container query: a reaction to the space the component gets.
  • `container-type: inline-size` adds containment on the horizontal axis and can change intrinsic width.
  • A container cannot style itself with its own query: you need an inner element.

Media queries measure the viewport; container queries measure the space of the component that contains them. A reusable component wants the second, because the same card lands in a 280 px sidebar and in a 900 px main column.

The same component in two contexts

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

.card {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}

@container cards (min-width: 30rem) {
  .card {
    grid-template-columns: 8rem 1fr;
    align-items: center;
  }
}

The card switches to two columns when its container passes 30rem, not when the screen passes 768 px. In the sidebar it stays single column, in the main column it goes to two: same CSS, no variant.

The same thing with a media query

/* funziona finché la card vive in una sola colonna di pagina */
@media (min-width: 48rem) {
  .card {
    grid-template-columns: 8rem 1fr;
  }
}

/* la variante che serve per la sidebar */
.sidebar .card {
  grid-template-columns: 1fr;
}

The media query answers the screen width, so the card widens even when it sits in a 300 px column. The fix is a context selector, and every new context adds another rule.

Who decides what

DecisionToolWhy
Number of page columnsMedia queryThe page knows the viewport
Card with one or two inner columnsContainer queryIt depends on the space received
Sidebar that becomes a drawerMedia queryIt is a global navigation decision
Text that changes scaleContainer queryIt depends on the block width

The record and the command

Blocks built to live in containers of different sizes: uploadthing-image-grid, the card category and the hero sections. The catalogue holds 1,556 blocks and 111,985 components from 1,472 libraries (1 November 2026).

# dove sono dichiarati i container nel progetto
grep -rn "container-type" --include="*.css" src/

# la card deve reggere anche a 320 px di viewport
npx playwright screenshot --viewport-size=320,800 http://localhost:3000 narrow.png

The three limits

  • A container cannot style itself: the query applies to descendants, so you need an inner element.
  • container-type: inline-size adds horizontal containment: a child with width: 100% no longer widens the container.
  • container-type: size needs an explicit height, otherwise the element collapses.

Known limit: without container-name the query hits the nearest ancestor container, and in a nested layout that container may not be the one you pictured. The explicit name costs one line and removes the ambiguity.

For the grid that hosts the containers see Flexbox or Grid, for shared tracks Subgrid. The same queries as utilities are in container queries with Tailwind.

Frequently asked questions

Do container queries replace media queries?

No. Media queries remain the tool for page structure and navigation choices; container queries serve components that live in different spaces.

Why does the card never change layout?

Almost always the container lacks container-type, or the container is the very element you want to style. The query watches descendants, not the container.

What is the difference between inline-size and size?

inline-size measures the horizontal axis only and needs no height. size measures both axes and needs an explicit height, otherwise the content collapses.

Do container queries cost performance?

The cost is small because the browser recalculates only the container subtree. The risk is containment: a container that no longer grows with its children breaks content-sized layouts.

How do I test them?

Resize the container, not the window: DevTools lets you select an element and read its size. A visual test at 280 px of sidebar covers the case the viewport never shows.