For twenty years, adapting interfaces relied on a single reference: the width of the browser window. Media queries interrogate the viewport, never the space actually available around a component. Container queries lift that limit by letting an element respond to the size of its container. The same block can then render compact in a sidebar and expanded in the centre of the page, without depending on screen resolution.
The idea: query the container, not the window
The mechanism works in two steps. A parent element is declared as a query container; its descendants can then condition their layout on that container’s size. The declaration uses the container-type property, optionally paired with a name via container-name.
.card-container {
container-type: inline-size;
container-name: card;
}
/* The card reacts to its container width, not the viewport */
@container card (min-width: 30rem) {
.card {
display: grid;
grid-template-columns: 12rem 1fr;
gap: 1.5rem;
}
}The inline-size value asks the browser to watch the inline dimension, that is the width in horizontal writing, while leaving the height free. It is the most common setting because it avoids the side effects of containing both axes. Like anchor positioning, container queries remove a slab of JavaScript once needed to measure elements.
Container units
Where media queries had viewport-relative units such as vw and vh, container queries introduce their own, computed against the size of the nearest query container. They let you size typography and spacing in proportion to the component.
| Unit | Reference | Viewport equivalent |
|---|---|---|
cqw | 1% of the container width | vw |
cqh | 1% of the container height | vh |
cqi | 1% of the inline size | vi |
cqb | 1% of the block size | vb |
cqmin | the smaller of the two | vmin |
cqmax | the larger of the two | vmax |
.card h3 {
/* Fluid heading, indexed on the container width */
font-size: clamp(1rem, 4cqi, 1.75rem);
}One component, two contexts
The payoff shows on a component reused in several places. The same product card, placed in a three-column grid or a full-width banner, takes on two layouts without a single global media query. The markup stays identical; only the container width decides.
<div class="card-container">
<article class="card">
<img src="/product.jpg" alt="" />
<div class="body">
<h3>Product name</h3>
<p>Short description.</p>
</div>
</article>
</div>Media queries and container queries: complementary
Container queries do not replace media queries, they complement them. The overall page structure, its layout gutters and its major breakpoints stay driven by the viewport, because they genuinely depend on screen size and device orientation. Container queries take over inside, at the level of reusable components, where the available width varies from one place to another. The two mechanisms coexist in the same stylesheet without interfering: it is up to the author to draw the line, keeping the viewport for overall layout and the container for the behaviour of interface building blocks.
Naming and nesting containers
The container shorthand combines type and name in a single declaration. Naming containers quickly becomes essential once they nest: an unnamed query targets the nearest ancestor container, which is not always the one expected. With an explicit name, the query unambiguously reaches the right level, even across several nested containers. This naming discipline mirrors that of a component system: each major page area, sidebar, main content or card footer, benefits from a stable name reused by the components it hosts.
/* Shorthand: type and name in one declaration */
.sidebar { container: rail / inline-size; }
.main { container: content / inline-size; }
/* The query targets the nearest named container */
@container rail (max-width: 20rem) {
.widget { font-size: 0.875rem; }
}Progressive deployment
The feature is available in recent versions of the major browsers, but a high-traffic site always keeps a share of visitors on older versions. Progressive enhancement answers this case: the @supports rule only turns on the conditional layout where container-type is recognised, and leaves a perfectly readable fallback elsewhere. The component stays functional everywhere, simply richer where the browser allows. This approach avoids the classic all-or-nothing trap, where a recent feature breaks rendering on older setups.
/* Progressive enhancement: only opt in when supported */
@supports (container-type: inline-size) {
.card-container { container-type: inline-size; }
}Typography and reflowing content
Beyond layout, container queries refine typography. Combined with the cq* units and the clamp() function, they produce text sizes that follow the component width without abrupt steps. Reading comfort is preserved when the same block moves from a narrow column to full width. One caveat: indexing a font size solely on the container can harm accessibility if the scale ignores the user’s preferences. The good practice is to bound the values with clamp() and keep a base in relative units, so that zoom and system settings are still respected.
What container queries do not do
An element cannot query itself: the query always targets an ancestor container, never the element that declares container-type. Containment also has a rendering cost, best reserved for components that benefit from it rather than the document root. Finally, style queries extend the idea to the container’s custom properties, useful alongside design tokens.
/* Style query: react to a custom property on the container */
@container style(--density: compact) {
.card { padding: 0.5rem; }
}A component stops being responsive at the scale of the page and becomes responsive at the scale of its place.
Watch out: declaring container-type: size contains both axes and requires an explicit height, otherwise the content can vanish. In the vast majority of cases, inline-size is the right choice.
The takeaway
Container queries move responsive logic from the document to the component. Together with the cq* units and, soon everywhere, style queries, they make interface libraries truly portable from one context to another. They round out a family of modern CSS features alongside View Transitions, each one trimming the reliance on JavaScript.
For a long time I duplicated components to handle their variants by location. Since I lean on container queries, I keep a single component and let it adapt wherever it sits: my CSS got leaner and so did my code reviews. It is one of the rare changes that simplifies both the code and the architecture. — Simon Janvier
Further reading
Reference documentation: Container queries on MDN.
