Tags: web-dev concept

Container Queries

Date: 2026-09-27


A component styles itself by the space it’s actually given, not by the size of the window. So one card works in a sidebar, a grid and a full-width row. The price is that the container’s size can’t depend on its content, and that constraint explains every “my container collapsed to zero”.


A container query applies styles based on the size of an ancestor that has been declared a container, rather than on the viewport as a media query does. It’s the mechanism that makes a component responsive to its own context — Viewport and Responsive Behaviour covers why viewport breakpoints were the wrong question for components.

The shape

MEDIA QUERY: the viewport is 1200px wide

┌──────── viewport 1200 ─────────────────────────────┐
│ ┌─ sidebar 300 ─┐ ┌─ main 900 ───────────────────┐ │
│ │ [card: WIDE]  │ │ [card: WIDE] [card: WIDE]    │ │
│ │  ← squashed   │ │                              │ │
│ └───────────────┘ └──────────────────────────────┘ │
└────────────────────────────────────────────────────┘

CONTAINER QUERY: each card asks its own container

│ ┌─ sidebar 300 ─┐ ┌─ main 900 ───────────────────┐ │
│ │ [card:STACKED]│ │ [card: WIDE] [card: WIDE]    │ │
│ └───────────────┘ └──────────────────────────────┘ │

The code

Two parts: declare a container, then query it from inside.

/* 1. the wrapper becomes a container. inline-size = query its width */
.card-slot {
  container-type: inline-size;
  container-name: card;          /* optional; unnamed queries hit the nearest container */
}
 
/* 2. the card changes shape when ITS SLOT is wide enough */
.card { display: grid; gap: 1rem; }
 
@container card (min-width: 420px) {
  .card { grid-template-columns: 160px 1fr; }   /* image beside text */
}
 
/* container query units: 1cqi = 1% of the container's inline size */
.card h3 { font-size: clamp(1rem, 4cqi, 1.5rem); }

An element can’t query itself — only an ancestor container. That’s why the query sits on a wrapper (.card-slot), or on a component’s outer element with the styles applied to its children.

Why the container can’t size to its content

container-type: inline-size applies size containment on the inline axis: the container’s width is worked out as if it had no children. Without that there’d be a loop — the query changes the children, the children change the container’s size, and that changes the query.

The consequence:

container in a block context        width comes from the parent → fine
container as a flex item, flex: 1   width comes from free space → fine
container with width: fit-content   width comes from content → collapses to 0
container that's a float or         same problem
  inline-block with no width

If a container collapses to zero or its content overflows, it’s in a shrink-to-fit context. Give it a width from outside (a grid track, flex: 1, width: 100%).

container-type: size contains both axes, so the height needs to come from outside too. It’s rarely what you want.

Media queries still have a job

Container queryMedia query
Component layout: card, product tile, promo blockPage layout: how many columns the page has
Anything reused in more than one slotUser preferences: prefers-reduced-motion, prefers-color-scheme
Device capability: hover, pointer: coarse

User preferences and input type only exist on the viewport. No container knows whether the user has a mouse.

Style queries — @container style(--variant: compact) — query a custom property’s value rather than a size, which lets a parent switch a child’s variant without a class [CHECK: current cross-browser support for style queries; size queries are the widely supported part].

Failure modes

  • Zero-width containers, above
  • Querying a container that isn’t there. An unnamed @container resolves to the nearest ancestor container. Move the component and it silently binds to a different, bigger one. Name containers that components depend on
  • Every element a container. Containment has a layout cost and breaks shrink-to-fit sizing. Declare containers on slots, not on everything
  • Duplicated breakpoints. A component with both media and container queries for the same change will disagree in some slot

Supported in all current major browsers for size queries [CHECK: confirm against current Baseline data if an older-browser audience matters] — Browser Compatibility.

Where it connects

  • Responsive Design — container queries are one of the tools that make breakpoints a last resort
  • Grid — auto-fit handles a lot of the same cases without any query
  • Component API Design — a component that adapts to its container needs fewer size props