Core concept 04
Container Queries
Respond to the container, not the viewport. The same component adapts to wherever it's placed — sidebar, grid, or full-width — with no changes to the markup.
The problem with media queries
Media queries ask "how wide is the viewport?" This works for page-level layout. But for individual components, it's the wrong question. A card component placed in a narrow sidebar needs different styles than the same card in a wide content area — and both can exist simultaneously on the same page.
How it works
You opt a parent element into being a query container using container-type. Child elements can then use @container to query that container's dimensions and apply styles accordingly.
/* Step 1 — declare the container */ .card-wrapper { container-type: inline-size; /* query the inline (horizontal) axis */ container-name: card-slot; /* optional name for specificity */ } /* Step 2 — query the container from inside it */ @container card-slot (min-width: 380px) { .card { grid-template-columns: 180px 1fr; } }
container-type values
| Value | Queries available | Use for |
|---|---|---|
inline-size | Width (inline axis) | Most responsive components |
size | Width and height | Components that need both axes |
normal | Style queries only | Default — not a size container |
Try it
The card below is inside a named container. Drag the slider to change the container width. Watch the layout adapt through three states — no viewport resize required.
Interactive demo — drag the slider
Container query
Responding to the container
Below 380px: stacked, no image.
380–560px: image + text, two columns.
Above 560px: image + text + aside, three columns.
Related
This column only appears when the container is ≥560px wide — from a single @container rule.
Real-world usage
The value of container queries is reusability. Write a component once; it adapts wherever it's placed.
/* The container — anything that holds a card */ .card-grid, .sidebar, .featured-slot { container-type: inline-size; container-name: card-slot; } /* The card — written once, adapts to all three contexts */ .card { display: grid; grid-template-columns: 1fr; } @container card-slot (min-width: 380px) { .card { grid-template-columns: 160px 1fr; } .card__image { display: block; } } @container card-slot (min-width: 560px) { .card { grid-template-columns: 180px 1fr 180px; } .card__aside { display: block; } }
Container queries work with tokens
Container queries and design tokens compose naturally. A card's layout responds to its container via @container, while its colours respond to the theme via semantic tokens. They're solving different problems and don't interfere with each other.
Container queries in the layer stack
Container query rules go in @layer components, alongside the base component styles they modify. Keeping them together makes the component's full responsive behaviour readable in one place.
@layer components { /* Container declaration */ .card-wrapper { container-type: inline-size; container-name: card-slot; } /* Base (smallest) state */ .card { background: var(--surface-raised); display: grid; grid-template-columns: 1fr; } /* @container rules go inside the same @layer block */ @container card-slot (min-width: 380px) { .card { grid-template-columns: 160px 1fr; } } }
Browser support
Container queries landed in all major browsers by early 2023 and are safe to use without fallbacks for modern browser targets.