Master CSS Container Queries v2 with multi-axis queries, style(), state(), and discrete queries for truly dynamic components that adapt to size, style, and state—no JavaScript required.
Container Queries v2: Dynamic Multi-Axis Layouts 🚀
- Container Queries v2 ships full cross-browser support in 2026, introducing
@container style(),@container state(), multi-axis queries, andcqi/cqb/cqhunits that make components respond to size + style + state simultaneously ⚙️. - Forget viewport-based media queries—v2 enables truly modular components that adapt their layout, typography, and behavior based on their actual container context, regardless of screen size 💡.
- This production guide covers enterprise-grade patterns for cards, dashboards, pricing tables, and complex grids that automatically reflow across any layout.
🎯 Container Queries v2 Superpowers
| v1 (2024) | v2 (2026) |
|---|---|
@container (min-width: 400px) | @container (width > 400px AND height > 300px) |
| Size only | Size + Style + State |
cqi units | cqi/cqb/cqh + style() + state() |
| Single-axis | Multi-axis queries |
🏗️ Complete Multi-Axis Dashboard Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Container Queries v2: Multi-Axis Dashboard</title>
<style>
/* SYSTEM RESET */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(135deg, #1e3a8a 0%, #3b82f6 100%);
min-height: 100vh;
padding: 2rem;
}
/* DASHBOARD CONTAINER */
.dashboard {
container-type: size style; /* v2: Track size AND style */
max-width: 1400px;
margin: 0 auto;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(20px);
border-radius: 24px;
padding: 2rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
/* RESPONSIVE GRID SYSTEM */
.stats-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
/* v2 STAT CARDS - Multi-axis responsive */
.stat-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 2rem;
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
/* Default: Compact mobile layout */
display: flex;
flex-direction: column;
gap: 1rem;
font-size: 0.9rem;
}
.stat-number {
font-size: 2.5rem;
font-weight: 800;
line-height: 1;
}
.stat-label {
opacity: 0.9;
font-weight: 500;
}
/* v2: SIZE + MULTI-AXIS QUERIES */
@container (min-width: 350px) and (min-height: 200px) {
.stat-card {
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
font-size: 1rem;
}
.stat-number {
font-size: clamp(2rem, 6cqi, 3.5rem); /* Container-relative! */
}
}
/* v2: RESPONSIVE TYPOGRAPHY WITH cqi */
@container (min-width: 450px) and (aspect-ratio > 1.2) {
.stat-card {
padding: 2.5rem;
}
.stat-number {
font-size: clamp(3rem, 8cqi, 4.5rem);
}
.stat-label {
font-size: 1.1rem;
}
}
/* v2: STYLE QUERIES - Detect container styling */
@container style(--layout: grid) {
.stats-grid {
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}
.stat-card {
border-radius: 16px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
}
/* v2: STATE QUERIES - Detect container state */
@container state(--theme: dark) {
.stat-card {
background: rgba(0, 0, 0, 0.3);
border-color: rgba(255, 255, 255, 0.4);
}
}
/* PRICING TABLE EXAMPLE */
.pricing-section {
container-type: size style;
max-width: 1200px;
margin: 4rem auto;
padding: 3rem;
display: grid;
gap: 2rem;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.pricing-card {
background: rgba(255, 255, 255, 0.1);
border-radius: 24px;
padding: 3rem 2rem;
text-align: center;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.2);
/* Default stacked layout */
gap: 1.5rem;
}
.price-header {
font-size: 1.5rem;
font-weight: 700;
}
.price-amount {
font-size: 3rem;
font-weight: 900;
background: linear-gradient(135deg, #fff 0%, #e0e7ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
/* v2: Multi-axis pricing card */
@container (min-width: 400px) and (min-height: 450px) {
.pricing-card {
display: flex;
flex-direction: column;
height: 100%;
}
.price-features {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: auto;
}
}
/* v2: Feature list becomes columns */
@container (min-width: 500px) and (aspect-ratio > 1.5) {
.price-features {
column-count: 2;
column-gap: 2rem;
}
.price-amount {
font-size: clamp(4rem, 10cqi, 6rem);
}
}
/* CONTROLS FOR DEMO */
.controls {
position: fixed;
top: 20px;
right: 20px;
display: flex;
gap: 1rem;
z-index: 1000;
}
.control-btn {
padding: 12px 20px;
border: none;
border-radius: 12px;
background: rgba(255, 255, 255, 0.2);
color: white;
font-weight: 600;
cursor: pointer;
backdrop-filter: blur(10px);
transition: all 0.2s ease;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
</style>
</head>
<body>
<!-- Demo Controls -->
<div class="controls">
<button class="control-btn" onclick="toggleLayout()">Grid/List</button>
<button class="control-btn" onclick="toggleTheme()">Dark/Light</button>
<button class="control-btn" onclick="resizeDashboard()">Resize</button>
</div>
<!-- MAIN DASHBOARD -->
<div class="dashboard" id="dashboard" style="--layout: flex;">
<h1 style="color: white; text-align: center; font-size: clamp(2rem, 5vw, 3.5rem); margin-bottom: 3rem;">
Container Queries v2 Dashboard ✨
</h1>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-number" data-value="12,456">12,456</div>
<div class="stat-label">Active Users</div>
</div>
<div class="stat-card">
<div class="stat-number" data-value="2.3M">$2.3M</div>
<div class="stat-label">Revenue</div>
</div>
<div class="stat-card">
<div class="stat-number" data-value="98.7%">98.7%</div>
<div class="stat-label">Uptime</div>
</div>
<div class="stat-card">
<div class="stat-number" data-value="847">847</div>
<div class="stat-label">Transactions</div>
</div>
</div>
</div>
<!-- PRICING SECTION -->
<section class="pricing-section">
<div class="pricing-card">
<div class="price-header">Starter</div>
<div class="price-amount">$29<small>/mo</small></div>
<ul class="price-features">
<li>âś“ 5 Projects</li>
<li>âś“ 10 Team Members</li>
<li>âś“ Basic Analytics</li>
<li>âś“ Email Support</li>
</ul>
</div>
<div class="pricing-card">
<div class="price-header">Pro</div>
<div class="price-amount">$99<small>/mo</small></div>
<ul class="price-features">
<li>âś“ 50 Projects</li>
<li>âś“ Unlimited Members</li>
<li>âś“ Advanced Analytics</li>
<li>âś“ Priority Support</li>
<li>âś“ API Access</li>
</ul>
</div>
<div class="pricing-card">
<div class="price-header">Enterprise</div>
<div class="price-amount">$299<small>/mo</small></div>
<ul class="price-features">
<li>âś“ Unlimited Everything</li>
<li>âś“ Custom Integrations</li>
<li>âś“ 24/7 Support</li>
<li>âś“ Dedicated Manager</li>
<li>âś“ On-Prem Option</li>
</ul>
</div>
</section>
<script>
function toggleLayout() {
const dash = document.getElementById('dashboard');
dash.style.setProperty('--layout', dash.style.getPropertyValue('--layout') === 'flex' ? 'grid' : 'flex');
}
function toggleTheme() {
document.documentElement.toggleAttribute('data-theme', 'dark');
}
function resizeDashboard() {
const dash = document.getElementById('dashboard');
dash.style.width = dash.style.width === '800px' ? '1400px' : '800px';
}
</script>
</body>
</html>
đź”§ v2 Syntax Deep Dive
1. Multi-Axis Queries
@container (min-width: 400px) and (min-height: 300px) {
/* Only triggers when BOTH conditions met */
}
2. Container Style Queries
/* Container sets custom property */
.dashboard {
container-type: style;
--layout: grid;
}
/* Component responds to container style */
@container style(--layout: grid) {
.card { border-radius: 16px; }
}
3. Container State Queries
@container state(--loading: true) {
.card { opacity: 0.5; }
}
4. Container Query Units (cqi/cqb/cqh)
.font-size: clamp(1.5rem, 4cqi, 3rem); /* 4% of container inline size */
.height: 20cqh; /* 20% of container height */
⚖️ Media Queries vs Container Queries v2
| Scenario | Media Query ❌ | Container Query v2 ✅ |
|---|---|---|
| Card in Sidebar | Wrong layout | Adapts to 300px width |
| Dashboard Widget | Viewport-based | Container-based |
| Pricing Table | Mobile-first hell | Multi-axis perfect |
| Reusable Components | Context-blind | Context-aware |
| Dynamic Grids | JS required | Pure CSS |
🎯 Production Checklist
âś… container-type: size style (track both)
âś… Multi-axis: width AND height queries
âś… cqi/cqb/cqh units for fluid typography
âś… @container style() for theme detection
âś… @container state() for loading/error states
âś… Combine with grid/flex for layouts
âś… @supports fallbacks for older browsers
🔥 Real-World Use Cases
- Dashboard Widgets - Reflow 1→2→3 columns automatically
- Pricing Cards - Stack→Row→Multi-column based on space
- Product Cards - Image left/right based on container width
- Navigation - Hamburger→Tabs→Full menu by container size
- Modals - Compact→Expanded based on modal width
- Container Queries v2 = End of viewport-based responsive hell.
- Components now adapt to their actual context, not screen size. Copy, paste, ship 🚀.
Continue Reading