Media queries ask about the viewport. But a card in a sidebar and the same card in a wide grid need different layouts at the same viewport width. Container queries let a component respond to the size of its container instead.
Size queries
Declare a container, then query it.
.card-wrapper {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
gap: 1rem;
}
@container card (min-width: 480px) {
.card {
grid-template-columns: 160px 1fr;
}
}
The same .card markup now stacks in a narrow sidebar and goes side by side in a wide column, wherever you place it. inline-size is almost always what you want; it only tracks the container's width.
Container query units
Units like cqi (1% of the container's inline size) let you scale things relative to the container.
.card h2 {
font-size: clamp(1rem, 4cqi, 1.75rem);
}
Style queries
You can also query a container's custom properties. This is useful for variants and theming.
.panel {
--tone: neutral;
}
.panel.danger {
--tone: danger;
}
@container style(--tone: danger) {
.panel-title {
color: #b91c1c;
}
}
Support for style queries on custom properties is not as universal as for size queries. Check caniuse before relying on it and provide a sensible default.
Scroll-state queries
Chromium (133+) supports querying scroll state, for example styling a sticky header only when it is stuck:
.header-wrap {
container-type: scroll-state;
position: sticky;
top: 0;
}
@container scroll-state(stuck: top) {
.header {
box-shadow: 0 2px 8px rgb(0 0 0 / 0.15);
}
}
Firefox and Safari do not support this yet, so treat it as progressive enhancement: the page must look fine without the shadow.
Using it in React and Tailwind
Container queries are plain CSS, so they work in any component. Tailwind CSS 4 supports them out of the box:
export function ProductCard({ product }: { product: Product }) {
return (
<div className="@container">
<article className="grid gap-4 @md:grid-cols-[160px_1fr]">
<img src={product.image} alt="" />
<div>
<h2 className="text-lg @lg:text-2xl">{product.name}</h2>
</div>
</article>
</div>
);
}
Practical advice
- Use media queries for page-level layout and container queries for reusable components.
- A container cannot query itself, so the styled element must be a descendant of the container.
- Name containers when you nest them, so queries target the one you mean.