Why Container Queries? The Problem They Solve
Before container queries, responsive design worked like this: the viewport is 600px wide, so we apply these styles. This works fine for full-page layouts, but it breaks down entirely for reusable components.
Imagine a card component. In a full-width grid it should be wide — image on the left, text on the right. In a sidebar it should be narrow — image on top, text below. In the old world, you'd have to write media queries that assume exactly where the card will live on the page. Move the card to a different layout context and the styles break.
Container queries solve this by letting the card say: "apply these styles when my container is wide" — not "when the viewport is wide". The card becomes truly portable. Drop it anywhere in any layout and it adapts to its actual available space.
Media queries: "The page is this wide, so..."
Container queries: "My container is this wide, so..."
Container queries make components context-aware instead of page-aware.
Basic Syntax: @container and container-type
There are two steps to using container queries. First, define a container on a parent element. Second, write @container rules on child elements.
/* Step 1: Define the container on the parent */
.card-wrapper {
container-type: inline-size;
}
/* Step 2: Write container-aware styles on the child */
.card {
display: flex;
flex-direction: column; /* default: stacked */
}
@container (min-width: 400px) {
.card {
flex-direction: row; /* side-by-side when container is wide */
}
}
<!-- Narrow sidebar: card stacks vertically -->
<aside class="sidebar">
<div class="card-wrapper">
<div class="card">...</div>
</div>
</aside>
<!-- Wide main content: card goes horizontal -->
<main class="main-content">
<div class="card-wrapper">
<div class="card">...</div>
</div>
</main>
<!-- Same HTML, same CSS — different results based on available space -->
You set container-type on the parent, but write @container rules targeting the children. An element cannot query its own size — only its container's size. This is why you typically need a wrapper element.
Container Types: inline-size, size, normal
The container-type property controls what kind of containment is established:
/* inline-size: respond to width (horizontal axis)
Most common — use this for most components */
.container {
container-type: inline-size;
}
/* size: respond to both width AND height
Requires the element to have a defined height
Less common — use for fixed-height contexts */
.fixed-height-container {
container-type: size;
height: 400px;
}
/* normal: no size containment
Style queries still work, size queries do not
The default value */
.style-only-container {
container-type: normal;
}
Use inline-size in almost all cases. It creates containment on the inline (horizontal) axis, which is what you need for width-based responsive components. The size type also requires height containment which means the element must have a defined height — this limits its usefulness for most layout scenarios.
Naming Containers
You can name containers with the container-name property (or the shorthand container). Named containers let you target a specific ancestor rather than the nearest container.
/* Using shorthand: container: name / type */
.sidebar {
container: sidebar / inline-size;
}
.main-content {
container: main / inline-size;
}
/* Target a specific named container */
@container sidebar (min-width: 300px) {
.widget { font-size: 1rem; }
}
@container main (min-width: 700px) {
.widget { font-size: 1.2rem; }
}
/* Without a name, @container targets the nearest container ancestor */
@container (min-width: 400px) {
.card { flex-direction: row; }
}
Named containers are useful when you have nested containers and need to target a specific ancestor. For example, a card inside a grid inside a sidebar — you can write rules that respond to the sidebar's width regardless of the grid in between.
Container Query Units (cqw, cqh, cqi, cqb)
Container query units are relative length units based on the nearest container's dimensions — like vw/vh but for containers instead of the viewport. They're extremely useful for fluid typography and spacing within components.
/* CQ unit reference:
cqw = 1% of container width
cqh = 1% of container height
cqi = 1% of container inline size (width in horizontal writing modes)
cqb = 1% of container block size (height in horizontal writing modes)
cqmin = smaller of cqi or cqb
cqmax = larger of cqi or cqb
*/
.container {
container-type: inline-size;
}
/* Fluid heading that scales with container width */
.card h2 {
font-size: clamp(1rem, 4cqw, 2rem);
/* minimum 1rem, scales to 4% of container width, max 2rem */
}
/* Padding that scales with container */
.card {
padding: clamp(12px, 3cqw, 32px);
}
/* Image that fills container proportionally */
.card-image {
width: 100%;
height: 30cqw; /* 30% of container width */
object-fit: cover;
}
vw is 1% of the viewport width. cqw is 1% of the nearest container's width. Use cqw when you want text or spacing to scale with the component's context rather than the page. This makes components truly self-contained.
Style Queries: @container style()
Style queries are a newer addition that lets you query the computed value of a CSS custom property on a container, rather than its size. This enables powerful theming patterns.
/* Set a custom property on the container */
.card-wrapper {
container-type: normal; /* style queries work with normal */
--card-theme: default;
}
.card-wrapper.featured {
--card-theme: featured;
}
/* Query the custom property value */
@container style(--card-theme: featured) {
.card {
background: linear-gradient(135deg, var(--accent), var(--accent2));
color: white;
}
.card-badge {
display: block; /* show a "Featured" badge */
}
}
@container style(--card-theme: default) {
.card-badge {
display: none;
}
}
Style queries for custom properties are supported in Chrome 111+, Safari 18+ and Edge 111+. Firefox support is still catching up. Check caniuse.com for the current status before using in production.
5 Practical Component Examples
1. Adaptive Card Component
.card-wrapper { container-type: inline-size; }
.card {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
}
.card-image {
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 8px;
}
.card-title { font-size: 1rem; font-weight: 700; }
.card-desc { font-size: 0.85rem; color: var(--muted); }
/* Side-by-side layout when container is wide */
@container (min-width: 420px) {
.card {
flex-direction: row;
align-items: flex-start;
}
.card-image {
width: 160px;
aspect-ratio: 1;
flex-shrink: 0;
}
.card-title { font-size: 1.15rem; }
}
/* Large card variant */
@container (min-width: 640px) {
.card { padding: 28px; gap: 24px; }
.card-image { width: 240px; }
.card-title { font-size: 1.4rem; }
}
2. Responsive Navigation
.nav-wrapper {
container: nav / inline-size;
}
.nav {
display: flex;
flex-direction: column;
gap: 4px;
}
.nav-link {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border-radius: 8px;
text-decoration: none;
}
.nav-label { display: none; } /* hidden by default in compact mode */
/* Show labels when nav container is wide enough */
@container nav (min-width: 200px) {
.nav { flex-direction: column; }
.nav-label { display: block; }
}
@container nav (min-width: 600px) {
.nav { flex-direction: row; }
.nav-link { padding: 8px 16px; }
}
3. Product Grid that Adapts to Columns
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}
.product-card-wrapper {
container-type: inline-size;
}
.product-card {
display: flex;
flex-direction: column;
}
.product-price { font-size: 1rem; font-weight: 700; }
.product-actions { display: none; } /* hidden until card is wide enough */
@container (min-width: 240px) {
.product-actions {
display: flex;
gap: 8px;
}
.product-price { font-size: 1.2rem; }
}
@container (min-width: 360px) {
.product-card { flex-direction: row; align-items: center; }
.product-image { width: 120px; flex-shrink: 0; }
}
4. Fluid Typography with cqw
.hero-wrapper {
container-type: inline-size;
}
.hero-title {
/* Scales from 1.5rem to 4rem based on container width */
font-size: clamp(1.5rem, 6cqw, 4rem);
font-weight: 800;
line-height: 1.1;
}
.hero-subtitle {
font-size: clamp(0.9rem, 2.5cqw, 1.4rem);
color: var(--muted);
}
.hero-cta {
padding: clamp(10px, 2cqw, 18px) clamp(20px, 4cqw, 36px);
font-size: clamp(0.8rem, 1.5cqw, 1rem);
}
/* No need for breakpoints — scales continuously with container */
5. Dashboard Widget
.widget-wrapper {
container: widget / inline-size;
}
.widget {
padding: 16px;
background: var(--surface);
border-radius: 12px;
border: 1px solid var(--border);
}
/* Compact mode (narrow widget) */
.widget-chart { height: 80px; }
.widget-legend { display: none; }
.widget-detail { display: none; }
/* Medium widget */
@container widget (min-width: 250px) {
.widget-chart { height: 140px; }
.widget-legend { display: flex; flex-wrap: wrap; gap: 8px; }
}
/* Full-size widget */
@container widget (min-width: 400px) {
.widget { padding: 24px; }
.widget-chart { height: 200px; }
.widget-detail { display: block; } /* show detailed stats */
.widget-legend { justify-content: space-between; }
}
Container Queries vs Media Queries
Container queries don't replace media queries — they complement them. Use each for what it's designed for.
| Use case | Media query | Container query |
|---|---|---|
| Page layout (sidebar vs full width) | ✓ Use media query | ✗ Viewport-dependent |
| Reusable component (card, widget) | ✗ Breaks when moved | ✓ Use container query |
| Typography based on page width | ✓ Use media query | ✗ Not appropriate |
| Typography based on component width | ✗ Can't know component width | ✓ Use cqw units |
| Dark/light mode | ✓ prefers-color-scheme | ✗ Not for this |
| Navigation (sidebar vs top nav) | ✓ Use media query | ✓ Or container query if nav is a component |
The rule of thumb: if a style depends on where something lives on the page, use a media query. If a style depends on how much space the component itself has, use a container query.
Browser Support and Fallbacks
Size container queries (@container with min-width, max-width etc.) are supported in Chrome 105+, Firefox 110+, Safari 16+, Edge 105+. Global support is approximately 90% as of July 2026.
/* Default styles (no container queries) */
.card {
display: flex;
flex-direction: column; /* stacked by default — works everywhere */
}
/* Enhancement: only applied in browsers that support container queries */
@supports (container-type: inline-size) {
.card-wrapper {
container-type: inline-size;
}
@container (min-width: 420px) {
.card {
flex-direction: row;
}
}
}
/* CQ units fallback */
.card-title {
font-size: 1.2rem; /* fallback for old browsers */
font-size: clamp(1rem, 4cqw, 1.8rem); /* modern browsers */
}
Build responsive layouts visually
Use the CSS Grid Generator and Flexbox Generator to build your layout foundation, then layer container queries on top for component-level responsiveness.
Open Grid Generator →Frequently Asked Questions
What are CSS container queries?
Container queries let you apply styles to an element based on the size of its parent container, using @container rules. Unlike media queries which respond to the viewport, container queries respond to the available space within the component's own context. You define a container with container-type: inline-size on a parent, then write @container styles on the children.
What is the difference between container queries and media queries?
Media queries respond to the viewport (browser window) width. Container queries respond to the size of a specific parent element. Use media queries for page-level layout changes. Use container queries for reusable components that need to adapt to their context. They complement each other — most projects use both.
What is container-type in CSS?
container-type defines what containment an element establishes. inline-size enables size queries based on the element's width (use this in almost all cases). size enables queries based on both width and height (requires a defined height). normal allows style queries only.
What are cqw, cqh, cqi and cqb units?
Container query length units: cqw = 1% of container width, cqh = 1% of container height, cqi = 1% of container inline size (width in LTR), cqb = 1% of container block size (height in LTR), cqmin = smaller of cqi/cqb, cqmax = larger. They work like vw/vh but relative to the container rather than the viewport. Perfect for fluid typography within components.
Can I nest container queries?
Yes — you can nest containers inside each other and each element queries its nearest container ancestor. You can also use named containers (container-name) to target a specific ancestor rather than the nearest one, which is useful in complex nested layouts.
What browsers support CSS container queries?
Chrome 105+, Firefox 110+, Safari 16+ and Edge 105+. Global support is approximately 90% as of mid-2026. Use @supports (container-type: inline-size) to progressively enhance and provide a working fallback for older browsers. CQ units (cqw, cqi etc.) have similar support.
Can an element query its own size?
No — an element cannot query its own size. It can only query the size of an ancestor container. This is why the pattern requires a wrapper: you set container-type on a parent wrapper, and write @container rules for the child component inside it. This design prevents circular dependency issues.