Container Queries
Components that respond to their container, not the viewport.
For twenty years, "responsive design" meant "respond to the viewport." That was always a hack. You don't want your card to know how big the browser is. You want it to know how big it is. A 320px-wide card should look the same in a sidebar as it does on a phone, no matter what the viewport says. That's what container queries are for, and they finally shipped in every evergreen browser by 2023.
The setup: declare a container
Container queries don't come for free. You have to mark an element as a containment context by giving it container-type. The two common values:
inline-size: contain in the inline dimension (width, for left-to-right languages). 95% of the time, this is what you want.size: contain in both dimensions. Rarely needed and has more layout side effects.
.card-wrapper {
container-type: inline-size;
}
/* Now any descendant can query the wrapper's width */
@container (min-width: 400px) {
.card { display: flex; gap: 16px; }
}
@container (max-width: 399px) {
.card { display: block; }
}Named containers for clarity
If you have nested containers and need to query a specific ancestor, give it a name with container-name:
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Or the shorthand */
.card-wrapper { container: card / inline-size; }
@container card (min-width: 400px) {
.card { /* ... */ }
}When container queries beat media queries
Imagine you build a product card. On the homepage it's in a three-column grid: narrow. In the cart it's a full-width list item: wide. With media queries, you'd need to know the layout context and write different rules. With container queries, the card adapts to whatever space it's given. The same component code works everywhere.
cqi, cqw, cqh: container-relative units
Container queries also added new units that scale with the container, not the viewport. Most useful:
cqi: 1% of the container's inline size (width).cqb: 1% of the container's block size (height).cqw,cqh: same as the above but tied to width/height specifically.
Combine with clamp() and you get fluid typography that scales with the component, not the viewport:
.card h3 {
/* font sizes with the card, not the page */
font-size: clamp(1rem, 4cqi, 1.5rem);
}A card that adapts to its container
This card has two layouts: a stacked version for narrow containers and a horizontal version when there is room. Drag the container widths around in the HTML by editing the wrapper styles, or just resize the preview pane.
container-type: inline-size introduces layout containment, which is what makes container queries possible. The browser has to lay out the container before its children. This is usually free, but in deeply nested grids it can cost. Don't putcontainer-type on every element "just in case." Add it where you need it.Container queries work great with components
The killer use case is design systems. A Card component ships with its own container queries built in. Drop it into any layout (sidebar, modal, three-column grid, full bleed) and it reformats itself. You don't have to pass prop variants like compact={true} from the outside. The component figures it out from context.
Style queries
There's a sibling feature called style queries for querying CSS custom properties: "if the --theme value on my container is dark, do this". It is no longer a preview. Chrome shipped it in 111, Safari in 18, and Firefox in 151, so all three engines have it.
@container style(--theme: dark) {
.card { background: #0b1020; color: #e2e8f0; }
}@container style(display: flex) does not work anywhere. That half of the spec exists on paper and in no engine. Set a custom property and query that.The other thing worth knowing: unlike size queries, style queries need no container-type. Every element is a style query container already, which means you can query a parent's custom property without adding containment.
Quiz
Before you can query a container's size with @container, what must you do?
Recap
- Container queries respond to a parent's size, not the viewport. Components become layout-agnostic.
- Opt in with
container-type: inline-size. Name withcontainer-namewhen you have nesting. - Query with
@container (min-width: 400px) { ... }. cqiand friends are container-relative units. Combine withclamp()for component-fluid typography.- Media queries are still right for layout-wide decisions; container queries are right for component-level ones.