I'd read about container queries for two years before I used one properly. The demos all
show a card that goes from stacked to side-by-side, you nod, and then you go back to writing
@media (min-width: 768px) because that's what your fingers know.
The thing that finally landed it for me was a card that appeared in three places on one page: full width in a hero band, two-up in a feature grid, and squeezed into a 20rem sidebar. Same component, same markup, three widths — and a media query cannot tell those apart, because all three are on a 1440px screen. The window is not the thing that changed.
What it actually costs
Two declarations. The parent opts in with container-type, and the child asks about
it:
.pf-container-q { container-type: inline-size; }
@container (min-width: 30rem) {
.pf-cq-row { flex-direction: row; align-items: center; }
}
The catch nobody mentions in the demos: container-type: inline-size means the
element's block size no longer depends on its contents in the way you might assume, and
if you put it on the wrong element you can get a container querying itself. Put it on the wrapper,
never on the component you are styling. I lost an afternoon to that.
Where it changed how I build
The real shift isn't the layout, it's that components stopped being page-aware. Before, a card that wanted to go horizontal on wide screens had to know it would be placed somewhere wide. That knowledge lived in the page's CSS, or worse, in a modifier class you had to remember to add — and the first time someone dropped that card into a sidebar, it broke, and the fix was another modifier.
Now the card knows how much room it has and decides for itself. Drop it anywhere. That is the
piece that makes a modular framework genuinely modular, and it's also why our documentation now
tells you to prefer cqi-flavoured thinking over breakpoints for anything reusable,
and to save the viewport queries for things that really are about the page — the site header, the
page shell.
| Question | Media query | Container query |
|---|---|---|
| What it measures | The window | The element’s own container |
| Same card in a sidebar | Cannot tell it from full width | Sees 20rem and stacks |
| Portable component | Needs a modifier per context | Drop it anywhere |
| Cost | None | A containment context on the wrapper |
| Unsupported engine | Keeps the base layout | Keeps the narrow layout |
Support, and the fallback you actually get
Chrome 105, Safari 16, Firefox 110. Old enough now that the question is mostly settled, but the
fallback matters more than the support table: an engine without container query support just
ignores the @container block, so the component keeps whatever it looks like at its
narrow default. Stacked. Readable. Not broken.
That's the correct fallback and it's worth designing for deliberately — write the narrow layout as the base, put the wide one in the query, and the old engine gets a slightly plainer page rather than a pile of overlapping boxes.
What this post does not cover
- Style queries, which are a separate feature at a different level of support.
- Container query units beyond a mention: the framework does not yet size type with
cqi.
Checked against container queries, support table. Code blocks are reproduced from the PolyFishCSS source files, not reconstructed from description. Figures are measured from the build they describe.
What this doesn’t cover
Container queries do not replace page-level breakpoints: a site header really is about the window. They also cost you a containment context, which changes how the element sizes in ways that bite if you put container-type on the component rather than its wrapper. Style queries and cqi units are a separate topic this post does not get into.
Sources: MDN on container queries, MDN on container-type and Can I Use. The query block is reproduced from PolyFishCSS-Utilities.css.