CSS Container Queries: The Responsive Design Feature We've Been Waiting For
For years, we've been stuck with media queries that respond to the viewport size. But what if your component needs to look different based on its container's size, not the whole screen? That's where container queries come in, and they're finally ready for production.
The Problem with Media Queries
Media queries work great for page-level layouts, but they fall apart when you have reusable components. Consider a card component:
.card {
display: flex;
flex-direction: column;
}
/* This breaks if the card is in a narrow sidebar! */
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}The card switches to horizontal layout based on the viewport width, not its actual available space. If you put this card in a narrow sidebar, it'll still try to go horizontal on wide screens, even when there's no room.
Enter Container Queries
Container queries let components respond to their parent container's size instead:
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
display: flex;
flex-direction: column;
}
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
}Now the card only goes horizontal when its container is at least 400px wide. Put it in a sidebar? It stays vertical. Put it in the main content area? It goes horizontal. Perfect!
Real-World Example: A Product Card
Here's a practical example I built recently:
.product-grid {
container-type: inline-size;
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.product-card {
display: grid;
gap: 0.5rem;
padding: 1rem;
background: white;
border-radius: 8px;
}
/* Compact layout for small containers */
.product-card img {
width: 100%;
aspect-ratio: 1;
object-fit: cover;
}
.product-card h3 {
font-size: 1rem;
}
.product-card .price {
font-size: 1.25rem;
font-weight: bold;
}
/* Enhanced layout when there's more space */
@container (min-width: 350px) {
.product-card {
grid-template-columns: 120px 1fr;
grid-template-rows: auto auto 1fr auto;
gap: 1rem;
}
.product-card img {
grid-row: 1 / -1;
height: 100%;
}
.product-card h3 {
font-size: 1.125rem;
}
}
/* Premium layout for larger containers */
@container (min-width: 500px) {
.product-card {
grid-template-columns: 200px 1fr;
}
.product-card img {
aspect-ratio: 4/5;
}
.product-card h3 {
font-size: 1.5rem;
}
.product-card .price {
font-size: 1.5rem;
}
}The same component automatically adapts whether it's in a full-width grid, a sidebar, or a modal dialog. No JavaScript, no component props, just pure CSS.
Container Query Units
Container queries also introduce new units:
cqw- 1% of container widthcqh- 1% of container heightcqi- 1% of container inline sizecqb- 1% of container block sizecqmin- smaller ofcqiorcqbcqmax- larger ofcqiorcqb
This lets you size things relative to the container:
.card-container {
container-type: inline-size;
}
.card h2 {
/* Font size scales with container width */
font-size: clamp(1rem, 5cqw, 2rem);
}
.card .icon {
/* Icon size relative to container */
width: 10cqi;
height: 10cqi;
}Browser Support
The best part? Container queries are supported in all modern browsers:
- Chrome 105+
- Safari 16+
- Firefox 110+
- Edge 105+
That's about 90% of users globally. For the remaining 10%, the components just use their default (mobile) layout, which is a perfectly acceptable fallback.
Practical Tips
1. Name Your Containers
Give containers meaningful names to make your code more readable:
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
@container sidebar (min-width: 300px) {
/* Styles */
}2. Container Type Matters
inline-size- Most common, queries container widthsize- Queries both width and height (use sparingly, can cause layout issues)normal- Default, no container queries
3. Combine with CSS Grid
Container queries + CSS Grid = chef's kiss
.layout {
display: grid;
gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.layout > * {
container-type: inline-size;
}Each grid item becomes a container, and child components respond to their actual size.
What I'm Building With It
I've been refactoring my component library to use container queries everywhere. Some highlights:
- Navigation menus that switch between horizontal and vertical based on available space
- Data tables that progressively hide columns in narrow containers
- Form layouts that adapt from single-column to multi-column
- Card grids that show different amounts of information based on card size
The Future is Bright
Container queries fundamentally change how we think about responsive design. Instead of designing for viewport breakpoints, we design components that adapt to any context.
This is huge for:
- Design systems and component libraries
- Third-party widgets and embeds
- Micro-frontends
- Any reusable UI component
If you haven't tried container queries yet, start with a simple card component. You'll immediately see why this is such a game-changer.
Resources
Have you used container queries in production yet? What's your favorite use case?