CSS

What two years of container queries in production actually taught us

CSS

Container queries were an early, flagged-behind-a-preference curiosity a few years ago, the kind of feature you'd demo excitedly at a team meeting while adding the caveat that it wasn't something you could actually ship to real users yet. With broad, unremarkable browser support for a while now, they've become a core layout tool rather than a novelty, and they've genuinely changed how we architect components, not just how we write the occasional clever layout trick.

Components are now written to describe their own responsive behavior based on the space they're given, not the viewport, which has made component libraries meaningfully more reusable across wildly different page layouts than viewport-based media queries ever allowed. A card component that needs to look right whether it's sitting in a wide three-column grid or squeezed into a narrow sidebar used to require either two separate component variants or a pile of layout-context props threaded down from wherever the component happened to be placed. With container queries, the component just queries its own container's width and adjusts, and the calling code doesn't need to know or care about that internal decision at all.

That shift has had a knock-on effect on how we structure our component library's documentation and testing, too. Instead of documenting a component's behavior at a handful of viewport breakpoints, which implicitly assumed the component always sat at the top level of the page, we now document and test it at a range of container widths independent of viewport, since the same component might legitimately need to render correctly at a hundred and eighty pixels wide inside a sidebar and at eight hundred pixels wide as a hero element on the same page, sometimes within the same user session.

The transition wasn't entirely friction-free. A handful of older components built around viewport media queries needed a genuine rewrite rather than a quick patch, since their internal layout logic assumed knowledge of the full page context that a container-scoped component simply doesn't have access to by design. That rewrite work is essentially finished across our shared component library at this point, and any new component gets written container-query-first from day one, with viewport media queries reserved for the small, deliberate category of page-level layout decisions where they're still genuinely the right tool.

Scoped CSS imports have matured alongside container queries in a way that's compounded the benefit rather than just adding a separate improvement. Being able to import a component's styles scoped strictly to that component's DOM subtree, without relying on naming convention discipline or a build-time CSS-in-JS abstraction to enforce the boundary, means a container-query-driven component can ship its responsive logic and its scoping guarantee together, natively, without any tooling layer standing between the two. A few years ago we'd have reached for a CSS-in-JS library specifically to get that scoping guarantee, accepting the runtime cost and build complexity that came with it. Native scoped imports get us the same guarantee for free, which has let us quietly retire a dependency that used to be considered essential infrastructure for any new component library.

The combination of these two features has changed a genuine architectural decision, whether to reach for a CSS-in-JS solution at all for a new project, into something we no longer seriously debate. Native CSS now covers the scoping and responsive-behavior needs that used to be the strongest argument in favor of a heavier JavaScript-based styling solution, and for any new project we start today, plain scoped CSS with container queries is the default starting point rather than one option among several.

← Back to the journal

Have a project in mind?
Let’s talk.

Tell us where you are and where you want to go. We'll map the fastest route between the two.

Currently accepting new clients