Introduction
For over a decade, responsive web design has relied on media queries based on the viewport—the browser window size. This approach works well for page-level layouts but breaks down when building reusable components that appear in various contexts. A navigation component might look perfect in a full-width header but completely break when placed in a narrow sidebar. Container queries solve this fundamental limitation by allowing styles to respond to a component’s container size, not the viewport.
The CSS Container Queries specification has achieved broad browser support in 2026, making it a practical choice for production applications. This capability fundamentally changes how we think about component architecture, enabling truly self-contained responsive components that adapt to any context they’re placed in.
This guide covers everything from basic setup to advanced patterns, helping you build robust component libraries that work at any size.
Understanding Container Queries
Container queries extend the familiar media query syntax but target element containers instead of the viewport.
The Core Problem
Traditional responsive design uses viewport-based media queries:
/* This card looks great full-width but breaks in narrow contexts */
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
@media (max-width: 600px) {
.card {
grid-template-columns: 1fr;
}
}
When this card appears in a 300px sidebar, the media query still applies because the viewport might be 1200px, even though the card has only 300px of space. The card’s layout becomes wrong for its actual context.
Container Queries Solution
Container queries solve this by making styles respond to container size:
/* Define a containment context */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Query the container instead of viewport */
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
}
Now the card adapts based on its own container size, regardless of where it appears in the page.
Container Types
The container-type property defines what aspects of the container are queryable:
/* Containment for inline size (width) only */
.container-inline {
container-type: inline-size;
}
/* Containment for block size (height) only */
.container-block {
container-type: block-size;
}
/* Containment for both dimensions */
.container-both {
container-type: size;
}
/* No containment (default) */
.container-none {
container-type: normal;
}
For most responsive design needs, inline-size suffices—it queries the container’s width regardless of viewport width.
Setting Up Containers
Proper container setup is essential for container queries to work correctly.
Basic Setup
Create a containment context with CSS:
.widget-container {
container-type: inline-size;
container-name: widget;
}
<div class="widget-container">
<div class="widget">
<!-- Widget content that responds to container -->
</div>
</div>
The container element must have explicit containment for the queries to work.
Containment Properties
Containment improves performance and enables the query behavior:
.card-wrapper {
/* Creates a containing block for descendants */
contain: layout style;
/* Also make it queryable */
container-type: inline-size;
container-name: card;
}
The contain property tells the browser to limit how much changes inside the container affect the rest of the page. This isolation is what makes container queries practical.
Naming Containers
Name containers to query specific ancestors:
<main class="main-content" style="container-name: main;">
<aside class="sidebar" style="container-name: sidebar;">
<div class="widget">
<!-- This queries the sidebar, not main -->
</div>
</aside>
</main>
/* Query the sidebar specifically */
@container sidebar (min-width: 300px) {
.widget {
/* Styles when in sidebar */
}
}
/* Query the main content */
@container main (min-width: 800px) {
.widget {
/* Styles when in main area */
}
}
Named containers enable targeting specific parent contexts.
Writing Container Queries
The syntax mirrors media queries with minor differences.
Querying Container Width
The most common query checks width:
@container (min-width: 400px) {
.component {
flex-direction: row;
}
}
@container (max-width: 399px) {
.component {
flex-direction: column;
}
}
Querying Container Height
For height-based layouts, use block-size:
@container (min-height: 200px) {
.component {
display: grid;
grid-template-rows: 1fr auto;
}
}
@container (max-height: 199px) {
.component {
display: flex;
flex-direction: row;
align-items: center;
}
}
Container Query Units
Container-relative units respond to container size:
| Unit | Description |
|---|---|
cqw |
1% of container width |
cqh |
1% of container height |
cqi |
1% of container inline size |
cqb |
1% of container block size |
cqmin |
Smaller of cqi or cqb |
cqmax |
Larger of cqi or cqb |
.component-title {
/* Title scales with container */
font-size: clamp(1rem, 5cqi, 2rem);
}
.component {
/* Padding based on container */
padding: calc(1cqi * 0.5);
}
These units eliminate the need for breakpoints in many cases.
Logical Properties
Use logical properties for internationalization:
@container (min-inline-size: 400px) {
.component {
/* Inline dimension = width in horizontal writing */
flex-direction: row;
}
}
@container (max-inline-size: 399px) {
.component {
flex-direction: column;
}
}
Practical Component Patterns
Container queries shine in real-world component scenarios.
Responsive Cards
Build cards that adapt anywhere:
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
display: flex;
flex-direction: column;
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
}
.card-content {
padding: 16px;
}
/* Large card - side-by-side layout */
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
.card-image {
width: 40%;
height: auto;
}
.card-content {
width: 60%;
}
}
/* Medium card - stacked with larger image */
@container card (min-width: 300px) and (max-width: 399px) {
.card-image {
height: 180px;
}
.card-content {
padding: 20px;
}
}
/* Small card - compact */
@container card (max-width: 299px) {
.card-image {
height: 120px;
}
.card-content {
padding: 12px;
}
.card-title {
font-size: 1rem;
}
}
Navigation Components
Navigation that adapts to any space:
.nav-container {
container-type: inline-size;
container-name: nav;
}
.nav-links {
display: flex;
gap: 16px;
}
/* Full navigation - horizontal */
@container nav (min-width: 600px) {
.nav-links {
flex-direction: row;
justify-content: flex-end;
}
.nav-item {
padding: 8px 16px;
}
.nav-logo {
margin-right: auto;
}
}
/* Medium - compact horizontal */
@container nav (min-width: 400px) and (max-width: 599px) {
.nav-links {
gap: 8px;
}
.nav-item {
padding: 6px 12px;
font-size: 14px;
}
}
/* Small - wrap */
@container nav (max-width: 399px) {
.nav-links {
flex-wrap: wrap;
justify-content: center;
}
.nav-logo {
width: 100%;
text-align: center;
margin-bottom: 12px;
}
}
Responsive Images
Images that scale intelligently:
.image-container {
container-type: inline-size;
container-name: image-wrapper;
}
.image-wrapper img {
width: 100%;
height: auto;
display: block;
}
/* Large - full display */
@container image-wrapper (min-width: 500px) {
.image-wrapper img {
object-fit: cover;
aspect-ratio: 16/9;
}
}
/* Medium - square */
@container image-wrapper (min-width: 300px) and (max-width: 499px) {
.image-wrapper img {
object-fit: cover;
aspect-ratio: 1;
}
}
/* Small - tall */
@container image-wrapper (max-width: 299px) {
.image-wrapper img {
object-fit: cover;
aspect-ratio: 3/4;
}
}
Form Components
Forms that work at any width:
.form-container {
container-type: inline-size;
container-name: form;
}
.form-field {
margin-bottom: 16px;
}
.form-label {
display: block;
margin-bottom: 4px;
font-weight: 500;
}
.form-input {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
/* Wide form - horizontal fields */
@container form (min-width: 500px) {
.form-row {
display: flex;
gap: 16px;
}
.form-field {
flex: 1;
}
}
/* Narrow form - stacked */
@container form (max-width: 499px) {
.form-row {
display: block;
}
}
Combining with Other Techniques
Container queries work alongside media queries and other CSS features.
Viewport + Container Queries
Use both for comprehensive responsive design:
/* Global layout based on viewport */
@media (max-width: 768px) {
.page-layout {
grid-template-columns: 1fr;
}
}
@media (min-width: 769px) {
.page-layout {
grid-template-columns: 250px 1fr;
}
}
/* Components based on their containers */
.component-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.component {
/* Component adapts regardless of page layout */
}
}
Container Queries + Grid
Powerful layouts combining both:
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
}
.dashboard-card {
container-type: inline-size;
}
@container (min-width: 350px) {
.dashboard-card {
padding: 24px;
}
}
@container (max-width: 349px) {
.dashboard-card {
padding: 12px;
}
}
CSS Custom Properties
Combine containers with custom properties:
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
/* Use container query units with custom properties */
--padding: calc(10cqi * 0.04);
padding: var(--padding);
font-size: clamp(0.875rem, 2cqi, 1.125rem);
}
@container card (min-width: 300px) {
.card {
--padding: 20px;
}
}
Browser Support and Fallbacks
Container queries have excellent support in modern browsers, but fallbacks ensure broader compatibility.
Browser Support
As of 2026, container queries are supported in:
- Chrome 105+
- Firefox 110+
- Safari 16+
- Edge 105+
Most modern projects can use container queries directly.
Feature Detection
Detect support with @supports:
.card-container {
container-type: inline-size;
}
/* Fallback for browsers without container queries */
@supports not (container-type: inline-size) {
.card {
/* Traditional responsive styles */
display: grid;
grid-template-columns: 200px 1fr;
}
@media (max-width: 400px) {
.card {
grid-template-columns: 1fr;
}
}
}
/* Container query styles */
@supports (container-type: inline-size) {
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container (max-width: 399px) {
.card {
grid-template-columns: 1fr;
}
}
}
Graceful Degradation
Design for fallbacks rather than blocking features:
.card-container {
/* Works even without container queries */
display: flex;
flex-direction: column;
/* Enhanced with container queries */
container-type: inline-size;
}
@container (min-width: 400px) {
.card-container {
flex-direction: row;
}
}
Advanced Patterns
More sophisticated patterns for complex applications.
Nested Containers
Containers can nest, with inner queries using outer containers:
.section-container {
container-type: inline-size;
container-name: section;
}
.widget-container {
container-type: inline-size;
container-name: widget;
}
.widget {
/* Queries widget container */
}
@container section (max-width: 600px) {
.widget {
/* Override when in narrow section */
}
}
Aspect Ratio Containers
Create aspect-ratio-based containers:
.aspect-container {
container-type: size;
container-name: aspect;
aspect-ratio: 16/9;
}
.content {
width: 100%;
height: 100%;
}
@container aspect (aspect-ratio < 1) {
.content {
/* Portrait orientation adjustments */
}
}
@container aspect (aspect-ratio > 2) {
.content {
/* Ultra-wide adjustments */
}
}
Container Style Queries
Query computed styles with style queries:
.theme-container {
container-type: inline-size;
container-name: theme;
--theme: light;
}
@container theme style(--theme: dark) {
.component {
background: #1a1a1a;
color: white;
}
}
@container theme style(--theme: light) {
.component {
background: white;
color: #1a1a1a;
}
}
Browser support for style queries is still emerging.
Performance Considerations
Container queries are highly performant, but understanding optimization helps.
Containment Benefits
The contain property provides performance benefits:
.container {
/* Layout containment prevents reflows from affecting rest of page */
contain: layout style;
/* Makes container queryable */
container-type: inline-size;
}
Minimizing Recalculations
Large numbers of container queries can impact performance:
/* Efficient - discrete breakpoints */
@container (max-width: 300px) { /* styles */ }
@container (min-width: 301px) and (max-width: 500px) { /* styles */ }
@container (min-width: 501px) { /* styles */ }
/* Less efficient - many overlapping queries */
@container (min-width: 100px) { /* styles */ }
@container (min-width: 150px) { /* styles */ }
@container (min-width: 200px) { /* styles */ }
Debugging Container Queries
Browser dev tools highlight containers:
/* Visual indicator during development */
.debug-container {
container-type: inline-size;
outline: 2px dashed red;
}
Remove debug styles in production.
Conclusion
Container queries represent a paradigm shift in responsive design. Rather than designing responsive components that happen to work in different page layouts, we can now design truly self-contained components that adapt intelligently to any space they’re given.
The key insight is that components should respond to their context, not the viewport. A card in a sidebar should look different from the same card in main content—not because of where it is in the page structure, but because of how much space it has available.
Start using container queries for new components, particularly those designed for reuse. The combination of container queries, CSS Grid, and modern CSS features enables component architectures that were previously impossible without JavaScript.
As browser support matures and developer familiarity grows, container queries will become as fundamental to responsive design as media queries are today. Building this capability into your workflow prepares you for the next era of component-based web development.
Comments