CSS Container Queries: Basics
Introduction to Container Queries
CSS Container Queries represent a significant evolution in responsive design, allowing elements to adapt based on their parent container's size rather than just the viewport size.
The Problem with Media Queries
Traditional responsive design relies on media queries, which have limitations:
- Viewport-centric: Media queries only respond to the overall viewport size
- Component reusability: Components can't adapt to their immediate context
- Layout flexibility: The same component might need different styles in different containers
Media Queries vs. Container Queries
Feature | Media Queries | Container Queries |
---|---|---|
Responds to | Viewport dimensions | Container dimensions |
Scope | Global (entire document) | Local (specific containers) |
Component reusability | Limited (fixed breakpoints) | High (adapts to container) |
Use case | Page-level layout changes | Component-level adaptations |
Basic Syntax and Usage
Defining Container Elements
Before using container queries, you need to define which elements should be considered containers:
/* Define a container */
.card-container {
container-type: inline-size; /* Enable container queries for inline axis */
container-name: card; /* Optional: Name the container for targeting */
}
/* Alternative container types */
.block-container {
container-type: block-size; /* Enable container queries for block axis */
}
.full-container {
container-type: size; /* Enable container queries for both axes */
}
/* Shorthand syntax */
.named-container {
container: card / inline-size; /* name / type */
}
Container Query Syntax
Once you've defined containers, you can write queries that target elements inside them:
/* Basic container query */
@container (min-width: 400px) {
.card-title {
font-size: 1.5rem;
}
.card-image {
float: left;
width: 40%;
}
}
/* Target a named container */
@container card (min-width: 600px) {
.card-content {
columns: 2;
}
}
/* Multiple conditions */
@container (min-width: 300px) and (max-width: 500px) {
.card-actions {
flex-direction: column;
}
}
Container Query Units
Available Units
Container queries introduce new relative units that are based on the container's dimensions:
Unit | Description | Example |
---|---|---|
cqw |
1% of container's width | font-size: 2cqw; |
cqh |
1% of container's height | margin-top: 3cqh; |
cqi |
1% of container's inline size | padding-inline: 2cqi; |
cqb |
1% of container's block size | margin-block: 1cqb; |
cqmin |
1% of container's smaller dimension | border-radius: 1cqmin; |
cqmax |
1% of container's larger dimension | max-width: 80cqmax; |
Using Container Query Units
.card-container {
container-type: inline-size;
}
.card-title {
/* Font size will be 4% of container width */
font-size: clamp(1rem, 4cqw, 2rem);
}
.card-image {
/* Image takes 50% of container width */
width: 50cqw;
/* Height maintains aspect ratio */
height: auto;
}
.card-content {
/* Padding scales with container size */
padding: 2cqw;
}
/* These units work even without @container queries */
vw
, vh
) scale with the viewport.
Practical Examples
Responsive Card Component
/* HTML */
<div class="card-container">
<div class="card">
<img class="card-image" src="image.jpg" alt="Card image">
<div class="card-body">
<h3 class="card-title">Card Title</h3>
<p class="card-text">Card description text goes here...</p>
<div class="card-actions">
<button class="btn-primary">Action</button>
<button class="btn-secondary">Cancel</button>
</div>
</div>
</div>
</div>
/* CSS */
.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: auto;
object-fit: cover;
}
.card-body {
padding: 1rem;
}
.card-title {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.card-actions {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
}
/* Container queries for responsive adaptations */
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
.card-image {
width: 40%;
}
.card-body {
flex: 1;
}
}
@container card (min-width: 600px) {
.card-title {
font-size: 1.5rem;
}
.card-text {
columns: 2;
column-gap: 1.5rem;
}
}
Responsive Navigation
/* HTML */
<div class="nav-container">
<nav class="main-nav">
<div class="nav-brand">Logo</div>
<ul class="nav-menu">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Contact</a></li>
</ul>
<button class="nav-toggle">Menu</button>
</nav>
</div>
/* CSS */
.nav-container {
container-type: inline-size;
container-name: nav;
}
.main-nav {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
padding: 1rem;
background-color: #f8f9fa;
}
.nav-brand {
font-weight: bold;
font-size: 1.25rem;
}
.nav-menu {
display: none;
list-style: none;
margin: 0;
padding: 0;
width: 100%;
margin-top: 1rem;
}
.nav-menu li {
margin-bottom: 0.5rem;
}
.nav-toggle {
display: block;
}
/* Container queries for responsive navigation */
@container nav (min-width: 600px) {
.nav-menu {
display: flex;
width: auto;
margin-top: 0;
gap: 1.5rem;
}
.nav-menu li {
margin-bottom: 0;
}
.nav-toggle {
display: none;
}
}
Browser Support and Fallbacks
Current Browser Support
Container queries are a relatively new feature, but support is growing:
- Chrome: Supported since version 105
- Firefox: Supported since version 110
- Safari: Supported since version 16
- Edge: Supported since version 105
Feature Detection
/* CSS feature detection */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
container-name: card;
}
/* Container queries here */
}
/* JavaScript feature detection */
if (CSS.supports('container-type', 'inline-size')) {
console.log('Container queries are supported!');
} else {
console.log('Container queries are not supported.');
// Apply fallback behavior
}
Fallback Strategies
/* Strategy 1: Start with mobile-first default styles */
.card {
display: flex;
flex-direction: column;
}
/* Add media queries as first-level fallback */
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
/* Then enhance with container queries */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
/* Override media query styles with container queries */
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
/* Strategy 2: Use JavaScript for older browsers */
if (!CSS.supports('container-type', 'inline-size')) {
// Add resize observer to mimic container queries
const containers = document.querySelectorAll('.card-container');
containers.forEach(container => {
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const width = entry.contentRect.width;
const card = entry.target.querySelector('.card');
if (width >= 400) {
card.classList.add('card-wide');
} else {
card.classList.remove('card-wide');
}
}
});
resizeObserver.observe(container);
});
}
Best Practices
When to Use Container Queries
- Reusable components that appear in different layout contexts
- Components with variable width constraints
- Card layouts, grids, and flexible content areas
- Widgets and UI components that need to adapt to their container
When to Use Media Queries
- Page-level layout changes that affect the overall structure
- Navigation patterns that depend on screen size
- Device-specific adaptations (e.g., touch vs. mouse interfaces)
- Print styles and other media-specific styles
Performance Considerations
- Limit container nesting: Avoid deeply nested containers when possible
- Be mindful of reflows: Container queries can trigger layout recalculations
- Use containment where appropriate: The
contain
property can improve performance
Next Steps
Now that you understand the basics of CSS Container Queries, you can explore more advanced topics in our CSS Container Queries: Advanced guide, including:
- Style queries and container query value types
- Complex layout adaptations with container queries
- Combining container queries with CSS Grid and Flexbox
- Advanced component examples and patterns
- Performance optimization techniques