Container queries are a revolutionary CSS feature that allows elements to respond to the size of their container instead of the viewport. This enables true component-based responsive design—components can adapt their layout based on available space, making them reusable in any context. Container queries solve the fundamental limitation of media queries: components can now be responsive regardless of where they're placed on the page.
Container Queries vs Media Queries
Understanding the difference between these two approaches is essential for modern responsive design.
Media Queries (Viewport-Based)
- Respond to viewport (browser window) size
- Same breakpoints everywhere
- Component looks same in sidebar and main area
- Can't reuse components in different contexts
- Global breakpoints for entire page
- Limited flexibility for components
Container Queries (Container-Based)
- Respond to container (parent) size
- Different breakpoints per context
- Component adapts to available space
- Fully reusable in any container
- Component-level responsiveness
- Perfect for component libraries
The Problem with Media Queries
Imagine a card component in your sidebar (300px wide) and main content area (800px wide). With media queries, both cards respond to viewport size. At 1000px viewport, both use the same layout—but the sidebar card is cramped while the main card has plenty of space.
Container queries solve this: Each card responds to its own container width. The sidebar card (300px) uses a compact layout, while the main card (800px) uses an expanded layout— automatically!
Browser Support: Container queries are supported in all modern browsers (Chrome 105+, Firefox 110+, Safari 16+, Edge 105+). They're ready for production use with fallback styles for older browsers.
Setting Up Containers
To use container queries, first define which elements are containers using the container-type property.
/* Define a container */
.container {
container-type: inline-size;
/* Makes this element a query container */
/* inline-size = responds to width (most common) */
}
/* Container types */
.container {
container-type: inline-size; /* Width only (most common) */
container-type: size; /* Width and height */
container-type: normal; /* Not a container (default) */
}
/* Named containers for specificity */
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
/* Shorthand */
.sidebar {
container: sidebar / inline-size;
/* name / type */
}
/* Multiple elements can be containers */
.card-container {
container-type: inline-size;
}
.sidebar {
container-type: inline-size;
}
.main-content {
container-type: inline-size;
}container-type Values
| Property | Values | Description |
|---|---|---|
inline-size | Width-based queries | Container responds to width changes (most common) |
size | Width & height queries | Container responds to both dimensions |
normal | Not a container | Default value, no container queries |
⚡ When to Use inline-size vs size:
Use inline-size (width) 99% of the time. It's more performant and handles the most common responsive scenarios. Only use size (width + height) when you specifically need to query both dimensions, which is rare.
Container Query Syntax
Use @container to query the container's size, similar to how @media queries the viewport.
/* Basic container query */
.card {
padding: 1rem;
}
@container (min-width: 400px) {
.card {
padding: 2rem;
display: grid;
grid-template-columns: 200px 1fr;
}
}
@container (min-width: 600px) {
.card {
padding: 3rem;
grid-template-columns: 250px 1fr;
}
}
/* Named container queries */
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
@container sidebar (min-width: 300px) {
/* Only applies to elements in 'sidebar' container */
.card {
background: lightblue;
}
}
/* Combining conditions */
@container (min-width: 400px) and (max-width: 800px) {
.card {
/* Styles for containers 400px-800px */
}
}
/* Container query units */
.card {
padding: 2cqw; /* 2% of container width */
font-size: 4cqi; /* 4% of container inline size */
}Container Queries in Action
Container Query Units
Container queries introduce new units that are relative to the container's size instead of the viewport.
Container Query Units
| Property | Values | Description |
|---|---|---|
cqw | 1% of container width | Container query width |
cqh | 1% of container height | Container query height |
cqi | 1% of container inline size | Usually width (most common) |
cqb | 1% of container block size | Usually height |
cqmin | 1% of smaller dimension | Min of cqi or cqb |
cqmax | 1% of larger dimension | Max of cqi or cqb |
/* Container query units */
.container {
container-type: inline-size;
}
.card {
/* Responsive padding based on container */
padding: 2cqi; /* 2% of container width */
/* Responsive font size */
font-size: 4cqi; /* 4% of container width */
/* Responsive spacing */
gap: 3cqw; /* 3% of container width */
margin-bottom: 2cqh; /* 2% of container height */
}
/* Combining with clamp for boundaries */
.heading {
font-size: clamp(1.5rem, 5cqi, 3rem);
/* Min 1.5rem, ideal 5% of container, max 3rem */
}
/* Using cqmin for square elements */
.avatar {
width: 15cqmin;
height: 15cqmin;
/* Always square, scales with smaller container dimension */
}Container Query Units
Practical Container Query Patterns
Pattern 1: Responsive Card Component
/* Reusable card that adapts to any container */
.card-wrapper {
container-type: inline-size;
}
.card {
padding: 1.5rem;
background: white;
border-radius: 8px;
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
margin-bottom: 1rem;
}
.card-title {
font-size: clamp(1.2rem, 4cqi, 2rem);
margin-bottom: 0.5rem;
}
.card-text {
font-size: clamp(0.9rem, 3cqi, 1rem);
color: #666;
}
/* Compact layout (< 300px) */
.card-meta {
display: none;
}
/* Medium layout (300px - 500px) */
@container (min-width: 300px) {
.card-meta {
display: block;
margin-top: 1rem;
font-size: 0.875rem;
color: #999;
}
}
/* Horizontal layout (> 500px) */
@container (min-width: 500px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
}
.card-image {
margin-bottom: 0;
height: 100%;
}
}Responsive Card Pattern
Pattern 2: Flexible Grid
/* Grid that adapts based on container size */
.grid-container {
container-type: inline-size;
}
.grid {
display: grid;
gap: 1rem;
}
/* Small container: 1 column */
.grid {
grid-template-columns: 1fr;
}
/* Medium container: 2 columns */
@container (min-width: 400px) {
.grid {
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
}
}
/* Large container: 3 columns */
@container (min-width: 600px) {
.grid {
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
}
/* Extra large: 4 columns */
@container (min-width: 800px) {
.grid {
grid-template-columns: repeat(4, 1fr);
}
}Flexible Grid Pattern
Named Containers
Named containers let you target specific containers when you have multiple containers in your layout.
/* Define named containers */
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
.main-content {
container-name: main;
container-type: inline-size;
}
/* Query specific containers */
@container sidebar (min-width: 300px) {
.card {
background: lightblue;
padding: 1rem;
}
}
@container main (min-width: 600px) {
.card {
background: lightgreen;
padding: 2rem;
display: grid;
grid-template-columns: 1fr 1fr;
}
}
/* Shorthand for container-name and container-type */
.sidebar {
container: sidebar / inline-size;
}
.main-content {
container: main / inline-size;
}Named Containers
Fallback for Older Browsers
Provide fallback styles for browsers that don't support container queries.
/* Mobile-first base styles (fallback) */
.card {
padding: 1rem;
/* Stacked layout for all browsers */
}
.card-image {
width: 100%;
margin-bottom: 1rem;
}
/* Container query enhancement */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 500px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
}
.card-image {
margin-bottom: 0;
}
}
}
/* Alternative: Feature detection with @supports */
@supports not (container-type: inline-size) {
/* Fallback to media queries for older browsers */
@media (min-width: 768px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1.5rem;
}
}
}🔧 Progressive Enhancement Strategy:
Start with mobile-first base styles that work everywhere. Use @supports to detect container query support and enhance the experience. For unsupported browsers, fall back to traditional media queries. This ensures your site works for everyone while providing the best experience for modern browsers.
Complete Practical Example
A complete example showing container queries in a real-world layout.
Complete Example - Same Component, Different Layouts
Key Takeaways
- Container queries: Respond to container size, not viewport
- container-type: inline-size (width), size (width + height), or normal
- Use
inline-sizefor 99% of cases @containersyntax similar to @media- Named containers with
container-namefor specificity - Container query units: cqi, cqw, cqh, cqb, cqmin, cqmax
- Components can be truly reusable in any context
- Perfect for component libraries and design systems
- Supported in all modern browsers (Chrome 105+, Firefox 110+, Safari 16+)
- Use @supports for fallback strategies
- Combine with media queries for best results
- Container queries solve viewport-based limitations
What's Next?
Congratulations! You've completed the Responsive Design section and now understand media queries, mobile-first approach, responsive units, and modern container queries. You have all the tools needed to create truly responsive, flexible websites that adapt to any device and context.
In the next section, Advanced Styling, we'll explore transforms, transitions, animations, filters, blend modes, and CSS variables. You'll learn how to add visual polish, smooth interactions, and dynamic effects to your responsive designs.
Container queries are a game-changer for component-based design. They enable truly modular, reusable components that work beautifully anywhere. Practice building container-aware components, and you'll create more flexible, maintainable designs!