CSS Container Queries in Practice: What Works and What Doesn't
Container queries finally arrived with full browser support in late 2023. After years of requests from developers, we can now style components based on their container’s size rather than viewport size. Two years into widespread availability, practical usage patterns have emerged.
The simple use case works brilliantly. A card component that adjusts its layout based on how wide its container is—stacking elements vertically in narrow containers, laying them out horizontally when there’s room. This is exactly what container queries were designed for, and it works as advertised.
You define a container using container-type: inline-size on a parent element, then use @container queries in the children to respond to that container’s width. It’s intuitive, it works reliably, and it solves real problems that media queries couldn’t handle well.
Component libraries are the obvious winners. If you’re distributing UI components that might be used in sidebars, main content areas, or full-width layouts, container queries let the components adapt automatically. No need for different component variants or JavaScript to detect container size.
But as developers have pushed container queries into more complex scenarios, limitations and gotchas have become apparent.
Performance can be an issue with deeply nested containers. If you have containers within containers within containers, each querying their parent’s size, the layout calculation overhead increases. Browsers handle this better than they did initially, but you can still create performance problems with overly complex container query hierarchies.
Container query units (cqw, cqh, cqi, cqb) are useful but can be confusing. They represent percentages of the container’s size, similar to viewport units but relative to the nearest container. Mixing container units with other units in calculations sometimes produces unexpected results.
The naming situation is awkward when you have multiple containers. You can name containers with container-name and query specific named containers with @container name (...). This is necessary for complex layouts, but it creates a dependency between parent and child components that can make components less reusable.
If a component expects to be inside a container named “card-wrapper” but gets used somewhere else, the container queries won’t match and the styling breaks. This coupling is sometimes necessary but it reduces the plug-and-play nature that makes components useful.
Integration with existing codebases has been messier than expected. Applications with years of accumulated media queries can’t just switch to container queries overnight. You end up with both, which creates cognitive overhead for developers who need to understand when to use which.
Server-side rendering and container queries don’t play well together. The server doesn’t know what size containers will be after CSS loads and layout happens. This means initial HTML might render with default styles, then jump when container queries apply after hydration. The flash of unstyled content is back in a new form.
JavaScript measurement still has its place. There are scenarios where you need to know the container size in JavaScript, not just in CSS. The ResizeObserver API exists for this, but it’s a different mental model than container queries. Developers end up using both, which feels like we haven’t fully solved the problem.
Aspect ratio queries are powerful but mind-bending. You can query based on aspect-ratio, which enables layouts that respond to container shape, not just size. But reasoning about aspect ratio breakpoints is harder than reasoning about width breakpoints. What’s the aspect ratio where a card should switch from horizontal to vertical layout? It’s not as intuitive as “at 600px wide.”
Container query polyfills for older browsers have gotten better, but they’re not perfect. They work by measuring containers with JavaScript and applying styles accordingly. This introduces layout shift and performance overhead. For applications that need to support older browsers, container queries create additional complexity.
The grid and flexbox interaction is interesting. Container queries work nicely with grid and flex layouts because you can query the container size and adjust grid templates or flex directions accordingly. But it’s easy to create circular dependencies where the container size depends on the content, and the content styling depends on the container size.
Browsers are smart enough to detect and break these cycles, but the fallback behavior when they do isn’t always what you’d expect. You need to design container queries carefully to avoid scenarios where the logic becomes circular.
Typography scaling with container queries works better than viewport-based scaling for components, but it requires careful tuning. Using container query units for font sizes can result in text that’s too small in narrow containers or too large in wide ones. You need multiple breakpoints or complex calculations to get smooth scaling.
Form layouts are a good use case. Forms often appear in different contexts—full-page, in modals, in sidebars. Container queries let form components adapt to whatever space they’re given without the parent context needing to know anything about form internals.
But form validation styling gets tricky. If validation messages cause the container to grow, and that growth triggers a container query that changes layout, you can get instability. The container size changes, which changes the query match, which changes the layout, which might change the container size again.
Debugging container queries is harder than debugging media queries. Browser dev tools have improved support, but figuring out why a container query isn’t matching often requires inspecting the container element, checking its size, verifying the container-type declaration, and confirming the query syntax. Media queries are more straightforward.
Design systems benefit from container queries, but they require more upfront architecture. You need to decide which elements are containers, what to name them, and what the container query breakpoints should be. With media queries, you had global viewport breakpoints. With container queries, each component potentially has its own breakpoints.
This flexibility is powerful but it can lead to inconsistency. Different developers might choose different breakpoints for similar components. Creating guidelines for container query usage becomes necessary to maintain system coherence.
Some developers are using container queries for everything, avoiding media queries entirely. This works for component-level styling but breaks down for true layout concerns. The page header that should be different on mobile vs desktop needs viewport queries, not container queries. You still need both.
The mental model shift is real. You need to think about components relative to their containers rather than relative to the viewport. For developers who’ve internalized viewport-based responsive design, this requires conscious adjustment.
Animation triggers based on container queries are possible but janky. If you want to animate when a container crosses a size threshold, you can use container queries to apply animation classes, but the timing often feels off because the query matching isn’t designed for animation use cases.
CSS transitions work fine with styles that change based on container queries, but complex orchestrated animations that depend on container size are better handled with JavaScript and Intersection Observer or ResizeObserver.
Progressive enhancement is straightforward—use container queries in @supports (container-type: inline-size) and provide fallback styling for browsers without support. But this means maintaining two styling approaches, which increases maintenance burden.
The real-world verdict after two years is that container queries are genuinely useful for component-level responsive design, but they’re not a magic solution that eliminates all responsive design complexity. They’re another tool in the toolbox, with specific use cases where they excel and others where traditional approaches work better.
For new projects, using container queries for components and media queries for page layout is becoming best practice. For existing projects, retrofitting container queries requires careful evaluation of whether the benefits justify the refactoring effort.
The technology delivered on its core promise—components that adapt to container size. But it also revealed that responsive design complexity doesn’t disappear just because you change which dimension you’re querying. The problems shift from viewport-based to container-based, and new challenges emerge.
Container queries are here to stay, and they’re improving web component design. But the hype cycle has completed, and we’re in the reality phase where the actual capabilities and limitations are becoming clear through production usage.