I spent the first half of my career focused mostly on Front End Development and considered myself quite the CSS guru. But as my roles have changed over the years and my attentions have been drawn to other technologies, CSS has grown and matured. Over the past several years, many long-desired features have appeared in the Specification. From CSS nesting and support for layers, to the grid, sub-grid, and the ‘:has()’ selector, CSS has grown to an even more powerful platform and gone are the days of struggling to center a div. Of these new parts of the spec, one I’ve had the pleasure of using on a project lately is Container Queries.
For over a decade, we’d relied on media queries to make our websites adapt to different screen sizes. But media queries have a blind spot: they only know about the viewport, not the actual space available to individual components. Container queries fix this.
The Problem with Media Queries
Imagine you’re working on a project that calls for the creation of a Product Card component shown in a list view in the main content area. The design then calls for you to put the same component in a sidebar area. With media queries, your options are limited.
In a 1024px viewport, your card component might be 800px wide. Plenty of room for a horizontal layout. But in the sidebar, you have 250px of horizontal space to work with. Everything needs to stack vertically. Media queries can only detect the “1024px viewport” and apply the same styles to both cards.
Years ago I would have just accepted my fate and would make two separate components: ProductCard and, begrudgingly, ProductCardSidebar.
Container Queries: Components That Know Their Size.
Container queries let elements respond to their parent container’s size instead of the viewport. Your card component doesn’t care if the screen is 1024 or 1920 pixels wide. It only cares how much space it has.
/* Make an element a container */
.card-wrapper {
container-type: inline-size;
}
.card {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Child elements can query it */
@container (min-width: 500px) {
.card {
flex-direction: row;
}
}
The card will automatically switch to a horizontal layout when its container is 500px or wider, regardless of viewport size.
Adding Tailwind 4.0
Previously only available via a plugin, as of version 4.0, Tailwind now has container queries baked in.
Basic Usage
Mark a parent element as a container with @container, then use the @ prefix variants on child elements that need to be container-aware.
<div class="@container">
<div class="grid @lg:flex @lg:gap-4">
<img class="w-full @lg:w-48" src="product.jpg" />
<div>
<h3 class="text-lg @lg:text-2xl">Product Name</h3>
<p class="text-sm @lg:text-base">Description here</p>
</div>
</div>
</div>
The breakpoint work just like regular responsive utilities in Tailwind:
@sm– 640px container width@md– 768px@lg– 1024px@xl– 1280px@2xl– 1536px
Named Containers for More Complex Layouts
When you have nest containers, you can give them names:
<div class="@container/main">
<div class="@container/card">
<h2 class="text-xl @lg/main:text-3xl @md/card:text-2xl">
Responsive to both containers
</h2>
</div>
</div>
Real Example of a Card That Works Everywhere
You can drop this into a narrow sidebar or a wide content area, and it will just work.
<div class="@container">
<article class="bg-white rounded-lg p-4
@md:p-6
@lg:flex @lg:gap-6">
<img class="w-full @lg:w-64 h-48 object-cover rounded"
src="product.jpg"
alt="Product" />
<div class="mt-4 @lg:mt-0 @lg:flex-1">
<h3 class="text-xl @lg:text-2xl font-bold">
Amazing Product
</h3>
<p class="mt-2 text-gray-600 text-sm @lg:text-base">
This card adapts based on how much space it actually has,
not based on the viewport size.
</p>
<div class="mt-4 flex gap-2 @md:gap-4">
<button class="flex-1 @md:flex-none @md:px-6 py-2 bg-blue-500 text-white rounded">
Buy Now
</button>
<button class="flex-1 @md:flex-none @md:px-6 py-2 border rounded">
Details
</button>
</div>
</div>
</article>
</div>
Where Container Queries Really Shine
- Component Libraries: Build components that truly work anywhere, without variants or props for every size.
- Dashboard Layouts: Widgets adapt to their grid cell size, not the screen size.
- Dynamic Content: User-generated layouts, CMS blocks, and resizable panels all work without special handling.
- Design Systems: One component, infinite contexts. No more “card-small”, “card-medium”, “card-large” variants.
The Cons
Container queries are powerful but have a few quirks:
- Performance: Containers create a new layout context, which has some performance overhead. Don’t make every element a container—be strategic.
- You still need media queries: Use media queries for page-level layout, container queries for component-level responsiveness.
- Circular dependencies A container can’t query its own size. It queries its parent’s size.
Browser Support
Container queries work in all modern browsers:
- Chromium-based browsers (Chrome, Edge) in version 105+
- Safari 16+
- Firefox 110+
According to CanIUse.com stats, that’s 92.5% of all global users.
Trying It For Yourself
If you’re using Tailwind 4.0, you already have container queries. Start by wrapping one component in a @container and see the possibilities. Stop thinking in terms of viewport size when it doesn’t make sense and start thinking in terms of actual available space. Your future self will thank you when you’re no longer maintaining variants of the same component.