Overview

Container queries let a component restyle itself based on its parent’s size, not the window’s. A card in a 240px sidebar and the same card in an 800px main column render with different layouts and no class-string conditionals. The umbrella sits at css; this page covers how to ship container-driven components in production.

Query the container, not the viewport

Reach for @container when a component should look different based on the space it has, not based on the device. Reach for @media when the rule is genuinely viewport-driven (touch targets, print, reduced motion).

.card {
  container-type: inline-size;
}
 
.card .title {
  font-size: 1rem;
}
 
@container (min-width: 32rem) {
  .card {
    display: grid;
    grid-template-columns: 12rem 1fr;
  }
  .card .title {
    font-size: 1.25rem;
  }
}

The same .card markup adapts to its slot. A page that drops cards into a 3-column grid and into a sidebar with one stylesheet is the typical win.

Set container-type: inline-size on the parent

Containment must be declared. Without container-type, @container rules match nothing.

  • inline-size: container queries on width only. Cheap; the common choice.
  • size: container queries on width and height. More expensive; require an explicit block size on the container.
  • normal: not a container; use to opt out.
.sidebar {
  container-type: inline-size;
}
.card {
  container-type: inline-size;
}

A page-level container around the main column is enough to drive nested components. Do not declare container-type on html or body; it shifts the containing block in ways that break sticky positioning and percentage heights.

Name containers when nesting

Without a name, @container matches the nearest containing-ancestor that opted in. Name containers when you have nested ones and need to target a specific level.

.cards {
  container-type: inline-size;
  container-name: cards;
}
.card {
  container-type: inline-size;
  container-name: card;
}
 
@container card (min-width: 24rem) {
  .card .title {
    font-size: 1.25rem;
  }
}
@container cards (min-width: 60rem) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}

Use the container shorthand to set both at once: container: card / inline-size.

Use cqi, cqw, and friends for container-relative sizing

Container query units size relative to the container, the way vw and vh size relative to the viewport.

  • cqi: 1% of the container’s inline size (the common one).
  • cqb: 1% of the container’s block size.
  • cqw and cqh: width and height variants.
  • cqmin and cqmax: the smaller and larger of the two axes.
.card .title {
  font-size: clamp(1rem, 4cqi, 1.5rem);
}

Fluid type inside a container scales with the container, not the viewport. This is what clamp(... + vw, ...) was approximating before container units shipped.

Adopt the responsive component pattern

A reusable component owns its breakpoints and its container declaration. The page composes components; the component decides how to render.

.media {
  container-type: inline-size;
  display: grid;
  gap: 1rem;
}
@container (min-width: 28rem) {
  .media {
    grid-template-columns: 8rem 1fr;
    align-items: start;
  }
}

The page that drops .media into a sidebar gets the stacked layout. The page that drops it into a wide column gets the row layout. Same markup, same class, no page-level conditionals. Pair with css-grid for the layout primitive and tailwind for the @container utility variants.

Browser support is shipped; the polyfill is gone

Container queries reached every evergreen browser by mid-2023. As of 2026, no polyfill is needed. Style queries (@container style(...)) and scroll-state queries are progressing through implementations; check Baseline before relying on them. The size-query subset, the one this page covers, is safe to ship without fallbacks.

Common pitfalls

  • Forgetting container-type. @container rules silently match nothing. The component looks like the queries are broken; they have no container.
  • Querying the wrong ancestor. Nested unnamed containers match the nearest. Name containers when nesting and the right rule applies.
  • container-type: size without a block size. The container collapses or breaks layout. Use inline-size unless you genuinely need height queries.
  • Containment on body. Breaks position: sticky and percentage heights site-wide. Move containment to the actual component wrapper.
  • Tab order changes on layout change. Reordering items with grid-template-areas between two queries can desync DOM and visual order; see accessibility.