CSS container queries are not per-component media queries
With 94% browser support and only 41% real-world adoption, container queries remain poorly understood. The mistake starts with the mental model.

Container queries are not a new feature. Browser support already hits 94%, and yet, according to the State of CSS survey cited by Victor Ayomipo on Smashing Magazine, 86% of devs know they exist but only 41.4% actually use them. Kevin Powell, at SmashingConf Amsterdam 2026, was blunt: adoption has been terrible. The problem, the article argues, isn't just adoption, it's incorrect use, and it stems from a misunderstanding: treating @container as if it were @media applied to a component.
They look similar at first glance, and that's exactly why the confusion persists. But they answer different questions, and that difference changes the way you think about layout.
The viewport is a proxy, and not always a reliable one
When you write @media (min-width: 1024px), the only thing the browser answers is: how wide is the screen right now? Nothing more than that. The article's example is the classic case that breaks in production: a .card component with display: flex from 1024px of viewport onward. Put that card in a 300px grid cell inside a 1920px monitor, and the media query still fires, because the viewport is still 1920px. The card switches to horizontal mode with 300px of actual space. Result: distorted content, overflow, squeezed text.
The media query isn't wrong, it's just, in Powell's words as quoted by the source, "dumb": not conceptually, but in the sense that it knows very little, and most people assume it knows more than it does. It looks outward, at the page's macro context.
The container query looks inward. The question changes to: how much space do I have at this specific point, right now? The same card rewritten:
.card-wrapper {
container-name: card;
container-type: inline-size;
}
@container card (min-width: 450px) {
.card {
display: flex;
flex-direction: row;
}
}Now the viewport is irrelevant. The card only becomes horizontal if the parent wrapper has at least 450px of inline space (horizontal, in left-to-right writing). If it doesn't, it stays in the default block display. The component decides based on the space it was given, not on an abstract number from the screen.
Macro versus micro: the distinction that organizes the decision
The most useful way to separate the two, as proposed in the article, is by layout type. Media queries serve macro layout: page structure, a header that spans the window, footer, main grid, system preferences (prefers-color-scheme), device capability (touch). Everything that's true for the whole page.
Container queries serve micro layout: cards, widgets, forms, internal navigation, anything that lives inside the macro and needs to adjust to the space it's been allocated. "Page layout" versus "component layout," the text sums up.
The argument behind it is solid, and it's where accessibility and robustness come in: an element shouldn't turn into "tablet size" just because the width passed 768px. It should change when it has room to change, whether on a phone or inside a desktop sidebar. The data point that closes the reasoning: there are more than 2,300 unique viewport sizes on the modern web. Trying to predict all of them with fixed breakpoints is a losing battle. Tying the logic to the content and to the available container fixes the root of the problem, not the symptoms.
Component-relative units: typography that scales with the component
Fluid typography is the case where a lot of people still use vw out of habit. The problem:
.card-title {
font-size: clamp(100%, 1rem + 2vw, 24px);
}This works until the component ends up in a sidebar, where the viewport has nothing to do with the actual space. The font scales against the wrong reference and ends up too big or too small. Container queries bring their own units, cqi, cqw, cqb among others, relative to the container instead of the screen:
.card-title {
font-size: clamp(1rem, .5rem + 3cqi, 2rem);
}Now the text's responsiveness is self-contained within the element's container. Move the component wherever you want: the typography follows the space it actually occupies.
The trick media queries never pulled off: detecting flexbox wrap
This is the most interesting point in the source, and it matters for anyone building menus and action bars. Flexbox does automatic wrapping with flex-wrap: wrap, but plain CSS has no way to know when the wrap happened. There's no :wrapped and no @media (flex-wrapped: true). Media queries only see the browser window, they're structurally blind to internal layout events. To detect wrapping, the traditional path is JavaScript with ResizeObserver.
The article shows a technique, attributed to Kevin Powell, that solves this with CSS alone. The idea: register each flex item as a container and let it grow with flex-grow when it wraps to a new line.
.flex-layout {
display: flex;
flex-wrap: wrap;
}
.flex-item {
container-type: inline-size;
flex: 1 1 390px;
}
.card {
display: flex;
flex-direction: column;
background: #f4f4f4;
}
@container (min-width: 600px) {
.card {
flex-direction: row;
align-items: center;
background: #e2f0d9;
}
}The logic: with room to spare, the two items sit side by side, each taking up half the parent's width. When space gets tight, the second item wraps to the line below, and since flex-grow is active, it stretches to fill almost the entire width of the parent. That sudden width expansion is exactly what the container query detects, triggering the new styles. It's not bulletproof, the author himself warns, but it removes JavaScript from a common case.
Three side effects before you go migrating
Container queries have concrete caveats that break code, and the article lists the main ones.
1. A container needs an extra wrapper. A container can't query itself, that would be an infinite loop. This code does not work:
.card {
container-name: card;
container-type: inline-size;
}
@container card (min-width: 400px) {
.card { display: flex; }
}You need a parent element (.cards) as the container and to apply the style on the descendant (.card). With media queries this doesn't matter, because the viewport is always available as a reference.
2. Querying size can collapse the layout. When using container-type: size (which includes the block axis, vertical), the browser calculates dimensions without looking at the children. Without an explicit height, min-height, or aspect-ratio, it sets height to 0px, even with content inside. That's why the recommendation is to prefer inline-size, unless you really need to query block size.
3. They don't accept custom properties. You can't query against a CSS variable:
:root { --breakpoint-lg: 1600px; }
/* DOES NOT WORK */
@container (min-width: var(--breakpoint-lg)) { }The reason is the cascade: a container query that depends on a custom property could alter that same property, creating a circular dependency. The limitation is intentional.
When each one makes sense
The article's conclusion is balanced, and it's the standard worth adopting: no project should swap @media for @container wholesale. The practical question is about separation of concerns. Use a container query when the component lives in more than one layout context, the same card in a full-width grid and in a narrow sidebar. Use a media query when the component only exists at the page level, like the main navigation that always sits at the top and is directly influenced by the viewport.
What container queries deliver is the ability to detect when a specific component's context changes, and adjust the style based on the actual space available. For those building design systems and reusable components, it's the missing piece for "reusable" to truly mean that the component behaves well anywhere, without depending on global breakpoints that have no idea where it ended up.
Translated from the Brazilian Portuguese original · Read the original
Figma Dev Mode MCP Server exposes nodes, variables, and tokens to AI agents
The official Figma Help Center guide details what Copilot, Claude, and Cursor can read and write inside design files, and what that changes (and doesn't yet change) in the handoff with engineering.



