CSS Container Queries Guide
A complete reference for CSS Container Queries. Build truly modular, responsive components that adapt to their container size, not the viewport. Master @container rules, container types, query units, and practical patterns.
Introduction
Container Queries allow elements to respond to the size of their container, not the viewport. This enables truly modular, reusable components that work anywhere.
Why Container Queries?
❌ Media Queries
Components respond to viewport size.
- Global breakpoints
- Same component, different contexts = problems
- Hard to create reusable components
✅ Container Queries Modern
Components respond to container size.
- Component-based breakpoints
- Works in any context
- Truly modular & reusable
Real-World Example
/* Same card component works perfectly in:
- Wide main content area
- Narrow sidebar
- Multi-column grid
All without different CSS classes! */
.container {
container-type: inline-size;
}
.card {
display: block;
}
/* When container is wide, use grid layout */
@container (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Container Query Basics
Two steps: define a container, then query it.
Step 1: Define a Container
/* Make an element a container */
.container {
container-type: inline-size;
}
/* With a name (optional but recommended) */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
/* Shorthand */
.main {
container: main / inline-size;
/* container: name / type */
}
Step 2: Query the Container
/* Query any parent container */
@container (min-width: 400px) {
.card {
display: flex;
}
}
/* Query named container */
@container sidebar (min-width: 300px) {
.widget {
font-size: 1.2rem;
}
}
/* Multiple conditions */
@container (min-width: 400px) and (max-width: 800px) {
.card {
padding: 2rem;
}
}
Complete Example
<!-- HTML -->
<div class="container">
<article class="card">
<img src="image.jpg" alt="Card image">
<div class="card-content">
<h3>Card Title</h3>
<p>Card description...</p>
</div>
</article>
</div>
/* CSS */
.container {
container-type: inline-size;
container-name: card-container;
}
/* Small: stacked layout */
.card {
display: flex;
flex-direction: column;
}
/* Medium: horizontal layout */
@container card-container (min-width: 400px) {
.card {
flex-direction: row;
}
.card img {
width: 200px;
}
}
/* Large: grid layout */
@container card-container (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 300px 1fr;
gap: 2rem;
}
}
Container Types
Different types of containment for different use cases.
inline-size
/* Most common: query container's inline size (width in horizontal writing) */
.container {
container-type: inline-size;
}
/* Enables: width queries, cqw, cqi units */
@container (min-width: 500px) {
.card { /* ... */ }
}
Use for: Responsive components based on horizontal space
size
/* Query both dimensions */
.container {
container-type: size;
}
/* Can query width AND height */
@container (min-width: 500px) and (min-height: 300px) {
.card { /* ... */ }
}
/* Enables: width/height queries, all container units */
Use for: Components that need to adapt to both dimensions
Note: container-type: size requires the container to have a defined size. Children can't define the container's size.
normal
/* Default: no containment */
.container {
container-type: normal;
}
/* Cannot query size, but can query style (experimental) */
Comparison
/* inline-size - Most common ✅ */
.container {
container-type: inline-size;
/* - Queries: width (inline dimension)
- Units: cqw, cqi, cqmin, cqmax
- Content can grow container height */
}
/* size - Both dimensions */
.container {
container-type: size;
/* - Queries: width AND height
- Units: all (cqw, cqh, cqi, cqb, cqmin, cqmax)
- Container needs fixed size */
}
/* normal - No containment */
.container {
container-type: normal;
/* - No size queries
- For style queries (future) */
}
@container Syntax
Complete syntax reference for container queries.
Basic Syntax
/* Query any parent container */
@container (min-width: 400px) {
.element { /* styles */ }
}
/* Query named container */
@container sidebar (min-width: 300px) {
.element { /* styles */ }
}
/* Multiple conditions with AND */
@container (min-width: 400px) and (max-width: 800px) {
.element { /* styles */ }
}
/* OR conditions (not) */
@container not (min-width: 600px) {
.element { /* styles */ }
}
Size Queries
/* Width queries */
@container (min-width: 400px) { }
@container (max-width: 800px) { }
@container (width >= 400px) { } /* Range syntax */
@container (400px <= width <= 800px) { } /* Range */
/* Height queries (requires container-type: size) */
@container (min-height: 300px) { }
@container (max-height: 600px) { }
@container (height >= 300px) { }
/* Aspect ratio */
@container (aspect-ratio > 16/9) { }
@container (min-aspect-ratio: 1/1) { }
/* Orientation */
@container (orientation: portrait) { }
@container (orientation: landscape) { }
Range Syntax (Modern)
/* Old syntax */
@container (min-width: 400px) and (max-width: 800px) {
.card { /* ... */ }
}
/* ✅ New range syntax (cleaner!) */
@container (400px <= width <= 800px) {
.card { /* ... */ }
}
/* More examples */
@container (width >= 500px) { }
@container (width < 1000px) { }
@container (300px < width < 700px) { }
@container (height >= 400px) { }
Combining Queries
/* AND conditions */
@container (min-width: 400px) and (min-height: 300px) {
.card { /* ... */ }
}
/* Range syntax with AND */
@container (width >= 400px) and (height >= 300px) {
.card { /* ... */ }
}
/* NOT conditions */
@container not (min-width: 600px) {
/* Applies when width < 600px */
.card { /* ... */ }
}
/* Complex example */
@container sidebar (200px <= width <= 400px) and (orientation: portrait) {
.widget { /* ... */ }
}
Naming Containers
/* Single name */
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
/* Multiple names (space-separated) */
.card {
container-name: card layout;
container-type: inline-size;
}
/* Shorthand: name / type */
.main {
container: main / inline-size;
}
/* Query named container */
@container sidebar (min-width: 300px) {
.widget { /* ... */ }
}
/* Query any of multiple names */
@container card (min-width: 400px) {
.card-content { /* ... */ }
}
@container layout (min-width: 600px) {
.card-content { /* ... */ }
}
Container Query Units
Special units relative to container size.
Available Units
Using Container Units
.container {
container-type: inline-size;
}
.card {
/* Font size based on container width */
font-size: calc(1rem + 0.5cqw);
/* Padding based on container */
padding: 2cqw;
/* Gap based on container */
gap: 1cqw;
/* Width based on container */
width: 80cqw;
}
/* Responsive typography */
.heading {
font-size: clamp(1.5rem, 3cqw, 3rem);
}
/* Spacing that adapts */
.card-content {
padding: clamp(1rem, 2cqw, 3rem);
gap: clamp(0.5rem, 1cqw, 2rem);
}
Practical Examples
/* Container-relative layout */
.container {
container-type: inline-size;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(30cqw, 1fr));
gap: 2cqw;
}
/* Fluid typography */
.title {
font-size: clamp(1.5rem, 4cqw, 3rem);
margin-bottom: 2cqw;
}
.text {
font-size: clamp(0.875rem, 2cqw, 1.125rem);
line-height: 1.6;
}
/* Responsive spacing */
.card {
padding: clamp(1rem, 3cqw, 3rem);
border-radius: clamp(0.5rem, 1cqw, 1.5rem);
}
/* Icon sizing */
.icon {
width: clamp(24px, 5cqw, 64px);
height: clamp(24px, 5cqw, 64px);
}
cqw vs cqh vs cqi
/* In horizontal writing mode (default) */
cqi = cqw /* Inline = width */
cqb = cqh /* Block = height */
/* In vertical writing mode */
cqi = cqh /* Inline = height */
cqb = cqw /* Block = width */
/* Best practice: use cqi for writing-mode independence */
.container {
container-type: inline-size;
}
.element {
font-size: 2cqi; /* Works in any writing mode */
padding: 1cqi 2cqi;
}
Container Queries vs Media Queries
When to use each approach.
Side-by-Side Comparison
Media Queries
/* Responds to viewport */
@media (min-width: 768px) {
.card {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
/* Problems:
- Same card behaves differently
in sidebar vs main content
- Not reusable
- Breaks in new contexts */
Container Queries
/* Responds to container */
.container {
container-type: inline-size;
}
@container (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
/* Benefits:
- Works anywhere
- Truly reusable
- Context-aware */
When to Use Each
/* ✅ Use MEDIA QUERIES for:
- Page layout changes
- Navigation structure
- Global styles
- Viewport-dependent features */
@media (min-width: 1024px) {
.page-layout {
display: grid;
grid-template-columns: 250px 1fr;
}
.navigation {
/* Desktop nav */
}
}
/* ✅ Use CONTAINER QUERIES for:
- Component responsiveness
- Reusable modules
- Context-dependent layouts
- Truly modular design */
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
/* Component adapts to container */
}
}
/* 🎯 Best: Use BOTH together! */
/* Media queries for layout, container queries for components */
/* Page layout (media query) */
@media (min-width: 1200px) {
.grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Component behavior (container query) */
.grid-item {
container-type: inline-size;
}
@container (min-width: 300px) {
.card {
/* Adapts to grid cell size */
}
}
Combining Both
/* Layout with media queries */
@media (min-width: 768px) {
.page {
display: grid;
grid-template-columns: 300px 1fr;
}
}
@media (min-width: 1200px) {
.page {
grid-template-columns: 300px 1fr 300px;
}
}
/* Components with container queries */
.sidebar,
.main,
.aside {
container-type: inline-size;
}
.widget {
/* Adapts to its container, whether it's
sidebar, main, or aside */
}
@container (min-width: 250px) {
.widget {
padding: 1.5rem;
}
}
@container (min-width: 400px) {
.widget {
display: flex;
gap: 1rem;
}
}
Common Patterns
Practical patterns you'll use frequently.
Responsive Card Component
/* HTML:
<div class="card-container">
<article class="card">
<img src="..." alt="...">
<div class="card-body">
<h3>Title</h3>
<p>Description</p>
<button>Action</button>
</div>
</article>
</div> */
.card-container {
container-type: inline-size;
container-name: card;
}
/* Small: stacked */
.card {
display: flex;
flex-direction: column;
}
.card img {
width: 100%;
height: 200px;
object-fit: cover;
}
/* Medium: horizontal */
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
.card img {
width: 40%;
height: auto;
}
}
/* Large: enhanced layout */
@container card (min-width: 600px) {
.card {
display: grid;
grid-template-columns: 250px 1fr;
gap: 2rem;
}
.card-body {
padding: 1.5rem;
}
}
Responsive Grid Items
/* Grid that works anywhere */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
/* Each grid item is a container */
.grid-item {
container-type: inline-size;
}
/* Item content adapts to item width */
@container (max-width: 300px) {
.item-content {
font-size: 0.875rem;
padding: 1rem;
}
.item-image {
height: 150px;
}
}
@container (min-width: 301px) and (max-width: 500px) {
.item-content {
font-size: 1rem;
padding: 1.5rem;
}
.item-image {
height: 200px;
}
}
@container (min-width: 501px) {
.item-content {
font-size: 1.125rem;
padding: 2rem;
}
.item-image {
height: 250px;
}
}
Sidebar Widget
/* Widget works in any sidebar width */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.widget {
background: white;
border-radius: 8px;
padding: 1rem;
}
/* Narrow sidebar */
@container sidebar (max-width: 250px) {
.widget {
font-size: 0.875rem;
}
.widget-title {
font-size: 1rem;
}
.widget-icon {
display: none; /* Hide icons in narrow space */
}
}
/* Medium sidebar */
@container sidebar (min-width: 251px) and (max-width: 350px) {
.widget {
font-size: 1rem;
}
.widget-title {
font-size: 1.25rem;
}
}
/* Wide sidebar */
@container sidebar (min-width: 351px) {
.widget {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
}
.widget-icon {
font-size: 2rem;
}
}
Responsive Navigation
/* Navigation adapts to container width */
.nav-container {
container-type: inline-size;
container-name: nav;
}
.nav {
display: flex;
gap: 1rem;
}
/* Compact: icon only */
@container nav (max-width: 400px) {
.nav-item span {
display: none; /* Hide text */
}
.nav-item {
padding: 0.5rem;
}
}
/* Medium: icon + text */
@container nav (min-width: 401px) and (max-width: 600px) {
.nav-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
font-size: 0.75rem;
}
}
/* Full: horizontal layout */
@container nav (min-width: 601px) {
.nav-item {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
}
}
Form Layout
/* Form adapts to container */
.form-container {
container-type: inline-size;
}
.form-group {
margin-bottom: 1rem;
}
/* Narrow: stacked labels */
@container (max-width: 400px) {
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.form-label {
font-weight: 600;
}
}
/* Wide: horizontal labels */
@container (min-width: 401px) {
.form-group {
display: grid;
grid-template-columns: 150px 1fr;
align-items: center;
gap: 1rem;
}
.form-label {
text-align: right;
}
}
/* Extra wide: multi-column */
@container (min-width: 700px) {
.form-fields {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
}
.form-group.full-width {
grid-column: 1 / -1;
}
}
Data Table
/* Table to cards on small containers */
.table-container {
container-type: inline-size;
}
/* Small: card layout */
@container (max-width: 600px) {
.table thead {
display: none;
}
.table tbody tr {
display: flex;
flex-direction: column;
border: 1px solid #ddd;
margin-bottom: 1rem;
padding: 1rem;
border-radius: 8px;
}
.table td {
display: grid;
grid-template-columns: 120px 1fr;
gap: 1rem;
padding: 0.5rem 0;
}
.table td::before {
content: attr(data-label);
font-weight: 600;
}
}
/* Large: normal table */
@container (min-width: 601px) {
.table {
width: 100%;
border-collapse: collapse;
}
.table th,
.table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid #ddd;
}
}
Browser Support & Fallbacks
Current Support
92%+ global support — Container Queries work in all modern browsers as of late 2023.
Feature Detection
/* CSS feature detection */
@supports (container-type: inline-size) {
.container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
/* Container query styles */
}
}
}
/* Fallback for older browsers */
@supports not (container-type: inline-size) {
/* Use media queries instead */
@media (min-width: 768px) {
.card {
/* Fallback styles */
}
}
}
JavaScript Detection
// Check if browser supports container queries
if ('container' in document.documentElement.style) {
console.log('Container queries supported!');
} else {
console.log('Container queries NOT supported');
// Load polyfill or apply fallback
}
// Or check for container-type
if (CSS.supports('container-type: inline-size')) {
console.log('Container queries supported!');
}
Progressive Enhancement Pattern
/* Base styles (work everywhere) */
.card {
padding: 1rem;
border-radius: 8px;
}
/* Media query fallback */
@media (min-width: 768px) {
.card {
padding: 2rem;
display: grid;
grid-template-columns: 1fr 1fr;
}
}
/* Container query enhancement */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
/* Override media query with container query */
@container (min-width: 500px) {
.card {
padding: 2rem;
display: grid;
grid-template-columns: 1fr 1fr;
}
}
}
Best Practices
/* 1. Always provide base styles */
.component {
/* Mobile-first base styles */
}
/* 2. Use @supports for container queries */
@supports (container-type: inline-size) {
.container {
container-type: inline-size;
}
@container (min-width: 400px) {
.component { /* ... */ }
}
}
/* 3. Media queries as fallback */
@media (min-width: 768px) {
.component { /* Reasonable fallback */ }
}
/* 4. Test in older browsers */
/* Consider: Firefox < 110, Safari < 16, Chrome < 105 */