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.cqwandcqh: width and height variants.cqminandcqmax: 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.@containerrules 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: sizewithout a block size. The container collapses or breaks layout. Useinline-sizeunless 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-areasbetween two queries can desync DOM and visual order; see accessibility.