Mastering CSS Container Queries: The Complete 2024 Guide
Container queries revolutionized responsive design by allowing elements to respond to their container size rather than just the viewport. In 2024, container queries are mature, stable, and supported across all modern browsers, with powerful new features like style queries and scroll-state queries.
Why Container Queries Matter
Traditional media queries respond to viewport size, which works well for page-level layouts but breaks down when building reusable components that might appear in different contexts.
The Problem with Media Queries:
/* This card looks great at 400px viewport width... */
.card {
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
/* ...but what if it's in a narrow sidebar on a wide screen? */
With container queries, components adapt to their actual context:
.card {
display: flex;
flex-direction: column;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
Container Setup and Syntax
Basic Container Definition
/* Define a container */
.container {
container-type: inline-size; /* or size, normal */
container-name: my-container; /* optional but recommended */
}
Container Types
container-type: normal- Default, no containmentcontainer-type: size- Size containment (width/height queries)container-type: inline-size- Inline size containment (width in horizontal writing modes)container-type: scroll-state- For scroll-state queries
@container Syntax
/* Basic size query */
@container (min-width: 400px) {
.element { /* styles */ }
}
/* Named container */
@container my-container (min-width: 400px) {
.element { /* styles */ }
}
/* Multiple conditions with logical operators */
@container (width > 400px) and (height > 300px) {
.element { /* styles */ }
}
/* Using or/not */
@container (width > 400px) or (height > 600px) {
.element { /* styles */ }
}
@container not (width < 300px) {
.element { /* styles */ }
}
Container Query Units
Container queries introduce six new units that are relative to the container:
cqw- 1% of container widthcqh- 1% of container heightcqi- 1% of container inline size (logical width)cqb- 1% of container block size (logical height)cqmin- Smaller ofcqiorcqbcqmax- Larger ofcqiorcqb
Practical Examples
/* Fluid typography based on container */
.title {
font-size: clamp(1.5rem, 5cqi, 3rem);
}
/* Container-relative spacing */
.card {
padding: 2cqi;
margin-bottom: 1cqb;
}
/* Aspect ratios */
.media-container {
aspect-ratio: 16 / 9;
width: 100cqw; /* Fill container width */
}
/* Grid layouts */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(15cqw, 1fr));
gap: 2cqi;
}
Advanced Container Query Patterns
1. Size Queries with Logical Operators
/* Landscape containers */
@container (width > height) {
.card {
flex-direction: row;
}
}
/* Square containers */
@container (cqmin > 200px) {
.avatar {
border-radius: 50%;
}
}
/* Very wide containers */
@container (cqmax > 800px) {
.hero {
flex-direction: row-reverse;
text-align: left;
}
}
2. Style Queries (Container Properties)
Query CSS custom properties or computed styles of the container:
/* Theme-aware containers */
@container style(--theme: dark) {
.card {
background: #1a1a1a;
color: white;
}
}
/* Container with specific background */
@container style(background-color: blue) {
.text {
color: white;
}
}
3. Scroll-State Queries
Query the scroll state of containers:
.scrollable-content {
container-type: scroll-state;
container-name: content;
overflow: auto;
}
/* Show back-to-top when scrolled */
@container content scroll-state(scrollable: top) {
.back-to-top {
opacity: 1;
}
}
/* Style sticky elements when stuck */
.sticky-header {
position: sticky;
top: 0;
}
@container scroll-state(stuck: top) {
.sticky-header {
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
}
}
Real-World Component Examples
Responsive Card Grid
.cards-grid {
container-type: inline-size;
display: grid;
gap: 1rem;
}
/* Mobile: single column */
.card {
display: flex;
flex-direction: column;
}
/* Tablet: two columns */
@container (min-width: 600px) {
.cards-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* Desktop: featured card layout */
@container (min-width: 900px) {
.cards-grid {
grid-template-columns: 2fr 1fr 1fr;
}
.card:first-child {
grid-row: span 2;
}
}
Adaptive Navigation
.nav-container {
container-type: inline-size;
}
.nav {
display: flex;
gap: 1rem;
}
.nav-item {
flex: 1;
text-align: center;
}
/* Compact mode for small containers */
@container (max-width: 500px) {
.nav-item:not(.important) {
display: none;
}
.nav-menu-toggle {
display: block;
}
}
Typography Scale
.article {
container-type: inline-size;
}
.article h1 {
font-size: clamp(2rem, 8cqi, 4rem);
}
.article h2 {
font-size: clamp(1.5rem, 6cqi, 3rem);
}
.article p {
font-size: clamp(1rem, 4cqi, 1.25rem);
line-height: clamp(1.4, 2cqb, 1.8);
}
Browser Support (2024)
Container queries are now widely supported:
- ✅ Chrome 105+
- ✅ Firefox 110+
- ✅ Safari 16+
- ✅ Edge 105+
- ⚠️ Limited support in older versions
For progressive enhancement:
/* Fallback for unsupported browsers */
.card {
/* Mobile-first styles */
}
/* Enhancement for supported browsers */
@supports (container-type: inline-size) {
.card {
/* Container query styles */
}
}
Performance Considerations
Do's ✅
- Use container queries for component-level responsiveness
- Combine with CSS containment for better performance
- Use logical operators for complex conditions
- Leverage container units for fluid design
Don'ts ❌
- Don't nest containers too deeply (performance impact)
- Avoid querying every pixel change (use reasonable breakpoints)
- Don't rely on container queries for critical layout (use fallbacks)
Best Practices
- Establish Container Hierarchy
.page-layout {
container-type: size;
container-name: page;
}
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.content {
container-type: inline-size;
container-name: content;
}
- Use Descriptive Names
/* Good */
@container card-grid (min-width: 500px) { ... }
/* Avoid */
@container c1 (min-width: 500px) { ... }
- Combine with Custom Properties
:root {
--container-padding: 1rem;
--container-breakpoint: 600px;
}
@container (min-width: var(--container-breakpoint)) {
.element {
padding: var(--container-padding);
}
}
- Progressive Enhancement Strategy
/* Base styles (mobile-first) */
.component {
display: block;
}
/* Enhancement */
@supports (container-type: inline-size) {
.component {
display: flex;
}
@container (min-width: 600px) {
.component {
flex-direction: row;
}
}
}
Advanced Patterns
Container Query Polyfill Strategy
For older browsers, consider:
/* Use ResizeObserver as fallback */
.container {
/* Fallback logic */
}
@supports (container-type: inline-size) {
.container {
container-type: inline-size;
}
}
CSS-in-JS Integration
// With styled-components
const Container = styled.div`
container-type: inline-size;
.child {
font-size: 1rem;
@container (min-width: 400px) {
font-size: 1.5rem;
}
}
`;
Conclusion
Container queries have matured into a cornerstone of modern CSS, enabling truly modular and context-aware components. With support for size queries, style queries, and scroll-state queries, they provide unprecedented control over responsive design.
Start small by adding container queries to your existing components, then gradually build more sophisticated responsive systems. The future of CSS is contextual - embrace it!
What container query patterns are you most excited to implement? Share your experiences in the comments!