What is Container queries?
Container queries: Container queries let CSS style an element based on the size of its parent container rather than the viewport, so a card adapts to the column it sits in.
Media queries ask how wide the viewport is. Container queries ask how wide the box the component sits in is, which is the question a component actually needs answered. The same card is 320px wide in a sidebar and 900px wide in the main column of the same page; a media query cannot tell those apart, a container query can. The feature is supported in most browsers, and it changes how reusable components are written: the component carries its own breakpoints, and the page no longer needs to know them.
How it works
.card-grid > * {
container-type: inline-size;
container-name: card;
}
.card { display: grid; gap: 1rem; }
@container card (min-width: 28rem) {
.card { grid-template-columns: 160px 1fr; }
}
.card h3 { font-size: clamp(1rem, 4cqi, 1.5rem); }
- `container-type: inline-size` turns an element into a query container for its width. It applies size containment on that axis, so the container's width cannot depend on its contents; it must get its size from its parent, a grid track or an explicit value.
- `@container (min-width: ...)` styles descendants of the nearest matching container. A
container-namelets a rule target a specific ancestor instead of the nearest one. - Container units:
cqwandcqiare 1 percent of the container's width or inline size,cqhandcqbits height or block size, and they work anywhere a length does, which makes them a good base for fluid typography inside components. - Style queries (
@container style(--variant: compact)) switch on a custom property's value instead of a size. Support is newer and narrower than for size queries, so check before relying on them.
In Tailwind CSS v4
Tailwind v4 has container queries built in. @container on the parent sets container-type: inline-size, and @md:grid-cols-2 applies at the md container size, with the same names as the viewport breakpoints but prefixed with @. Named containers use @container/card on the parent and @md/card: on the descendants, and arbitrary sizes use @min-[30rem]:.
<article className="@container rounded-xl border border-border p-4">
<div className="grid gap-4 @md:grid-cols-[160px_1fr]">
<img className="aspect-video w-full rounded-lg object-cover" src="/cover.jpg" alt="" />
<div>
<h3 className="text-base font-medium @lg:text-lg">Quarterly review</h3>
<p className="text-sm text-fg-muted">Fits a sidebar or a full column.</p>
</div>
</div>
</article>
Pitfalls
- A container cannot size itself by its contents on the queried axis. A flex item with
container-type: inline-sizeand no width basis collapses. Give itflex: 1,min-width: 0or a grid track. - The container is not styled by its own query. Rules inside
@containerapply to descendants of the container, not to the container itself. Wrap the component in an extra element when it needs to style its own root. - Nesting resolves to the nearest ancestor container of the right type, which may be a parent component's wrapper rather than the one you meant; name containers when components nest.
MiniDev's Card header is declared a named container (@container/card-header) so the title, description and action can be rearranged with @sm/card-header: variants, and dashboard pieces such as StatCard, ChartCard and KpiRow are meant to be dropped into ResizablePanels and grids of any width. The layout category has the containers themselves.