CSS Container Queries: Components That Adapt Anywhere
Container queries let components respond to the space they're given, not the viewport. Here's how they work and when to reach for them.
Media queries have always had an awkward limitation: a component doesn’t know how much space it has, only how wide the viewport is. That works fine for page-level layout, but it breaks down as soon as you put the same component in two different contexts — a full-width feed and a narrow sidebar, say. Container queries solve that problem directly.
The core idea
With media queries, you write rules like “when the viewport is at least 768px wide, do this.” With container queries, you write rules like “when this element’s container is at least 400px wide, do this.” The component responds to the space it’s actually given, not some global signal.
This changes how you think about responsive components. A card, a product listing, a navigation widget — each one can have its own internal breakpoints that fire based on where it’s placed, not where the user’s window happens to be sized.
Setting up a container
To query an element, you first need to tell the browser which ancestor to measure. That’s container-type:
.card-wrapper {
container-type: inline-size;
}
inline-size (the most common value) tells the browser to track the element’s width for querying purposes. You can also use size to track both width and height, or normal to enable style queries without size tracking.
If you have multiple containers nested, you can give them names with container-name so you can target a specific ancestor in your query:
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
Writing the query
Once an ancestor is a container, you use @container to apply styles based on its size:
/* A card that stacks vertically by default */
.card {
display: flex;
flex-direction: column;
}
/* When the container is wide enough, go horizontal */
@container (min-width: 480px) {
.card {
flex-direction: row;
}
.card__image {
width: 200px;
flex-shrink: 0;
}
}
To target a named container:
@container sidebar (min-width: 300px) {
.card {
font-size: 0.875rem;
}
}
Without a name, @container queries the nearest ancestor that has a container-type set.
Container query length units
Container queries come with their own set of relative units:
| Unit | Meaning |
|---|---|
cqw | 1% of the container’s width |
cqi | 1% of the container’s inline size |
cqh | 1% of the container’s height |
cqb | 1% of the container’s block size |
cqmin | Smaller of cqi or cqb |
cqmax | Larger of cqi or cqb |
cqi is the most useful in practice — it sizes things relative to the container’s width regardless of writing mode, similar to how em is relative to font size:
.card__title {
font-size: clamp(1rem, 4cqi, 1.5rem);
}
That keeps the title proportional to the card’s width rather than the viewport.
A real example: the adaptive card
Here’s how a product card might work with container queries. Drop it in a three-column grid and it shows a compact stacked layout. Drop it in a full-width featured slot and it automatically switches to a horizontal layout:
.product-card-wrapper {
container-type: inline-size;
}
.product-card {
display: flex;
flex-direction: column;
gap: 1rem;
}
.product-card__image {
aspect-ratio: 16 / 9;
object-fit: cover;
}
/* Wide container: go side-by-side */
@container (min-width: 500px) {
.product-card {
flex-direction: row;
align-items: center;
}
.product-card__image {
width: 40%;
aspect-ratio: 1;
}
}
No JavaScript. No extra media queries. The card figures out what it looks like based on where it’s placed.
Style queries (emerging)
Beyond size, the spec also includes style queries — the ability to query computed CSS property values on a container. For example, applying different styles to children based on whether the parent has --featured: true set as a custom property. Style queries are newer and not yet as broadly supported as size queries, but they open up interesting patterns for design-token-driven theming.
Container queries vs. media queries
These two features are complementary, not competing. The right mental model:
- Media queries for page-level layout: the two-column vs. single-column split, showing or hiding a sidebar, adjusting the top navigation.
- Container queries for component-level layout: how a card renders in whatever slot it’s placed, how a widget adapts to a sidebar vs. a main area.
Most real projects will use both. Media queries set the structural skeleton; container queries handle the components inside.
Browser support
Container queries are well-supported across current versions of Chrome, Firefox, and Safari. If your browser compatibility target is modern evergreen browsers, you can use them in production today without a polyfill. The CSS Grid vs. Flexbox article covers the layout tools that often pair with container queries.
And if you’re using Tailwind CSS, v4 ships container query support built in — no plugin needed. Tight layout work also affects rendering performance, which the Core Web Vitals guide covers in depth.
The takeaway
Container queries are the feature responsive CSS was missing. They let you write components that are genuinely self-contained — portable across any layout context because they respond to their own available space rather than a global viewport signal. For component libraries, design systems, or any project where the same UI ends up in varied placements, they’re worth adopting now.
Keep reading
Takina · · 5 min read CSS will-change Explained: Compositing and Performance
The CSS will-change property hints the browser to prepare an element for an upcoming change, moving it to its own compositor layer. When to use it and when not to.
Takina · · 4 min read CSS Scroll-Driven Animations Explained
CSS scroll-driven animations tie keyframes to scroll position instead of a clock, running smoothly off the main thread. Here's how the timeline model works.
Takina · · 4 min read What Is the Beacon API? navigator.sendBeacon()
The Beacon API lets a page send one last async request as it unloads, without blocking navigation or racing the browser's page teardown.