CSS Container Queries: The Complete Guide for 2026
For over a decade, media queries were the only way to build responsive layouts in CSS. They work well for page-level decisions like switching a two-column layout to a single column on small screens. But they have a fundamental limitation: they respond to the viewport, not to the component's actual available space. A card component has no way to know whether it is sitting in a 300px sidebar or a 900px main content area. Container queries solve this problem entirely.
This guide covers everything you need to know about CSS container queries in 2026: the core syntax, container-type and container-name properties, size queries and style queries, container query units, nesting containers, real-world responsive component patterns, when to use container queries versus media queries, browser support, performance considerations, and a practical migration strategy from media queries to container queries.
The Problem: Media Queries Are Page-Level, Not Component-Level
Consider a card component that needs to display vertically when narrow and horizontally when wide. With media queries, you write something like this:
/* Media query approach: tied to the viewport */
.card {
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
This works when the card is full-width. But what happens when you place the same card in a narrow sidebar on a wide desktop screen? The viewport is 1200px, so the media query triggers the horizontal layout, but the sidebar is only 300px wide. The card renders horizontally in a 300px space and looks broken. You end up writing sidebar-specific overrides, and the component is no longer reusable.
Container queries eliminate this problem. Instead of asking "how wide is the viewport?", the component asks "how wide is my container?" The same card adapts correctly in every context without any overrides.
What Are Container Queries?
Container queries let you apply CSS styles to an element based on the size (or style properties) of its nearest containment context — a parent element that has been designated as a container. The key shift is from viewport-relative responsiveness to container-relative responsiveness.
There are two parts to using container queries:
- Define a container — set
container-typeon a parent element to establish a containment context. - Query the container — use
@containerrules to apply styles based on the container's dimensions or custom properties.
/* Step 1: Define the container */
.card-wrapper {
container-type: inline-size;
}
/* Step 2: Query the container */
.card {
display: flex;
flex-direction: column;
padding: 1rem;
}
@container (min-width: 450px) {
.card {
flex-direction: row;
gap: 1.5rem;
}
}
Now the card switches to horizontal layout when its container is at least 450px wide, regardless of the viewport size. Place it in a sidebar, a modal, a grid cell, or a full-width section — it always responds to its actual available space.
The container-type Property
The container-type property establishes an element as a query container. It accepts three values:
/* inline-size: containment on the inline axis only (width) */
/* This is the value you will use 95% of the time */
.wrapper {
container-type: inline-size;
}
/* size: containment on both inline and block axes (width + height) */
/* Requires the container to have an explicit height */
.fixed-panel {
container-type: size;
height: 400px;
}
/* normal: no containment (default, cannot be queried) */
.wrapper {
container-type: normal;
}
Use inline-size as your default choice. It enables width-based queries without requiring an explicit height on the container. The size value tracks both width and height, which is useful for fixed-dimension panels like dashboard widgets, but the container must have an explicit height set, otherwise it may collapse to zero.
Important: setting container-type establishes CSS containment on the element. This means the container cannot depend on its children for sizing in the contained axis. In practice, this is rarely an issue because containers are usually wrapper elements whose width is determined by the layout, not by their content.
The container-name Property
When you have multiple nested containers, you can name them to target a specific one in your queries:
/* Name containers individually */
.page-layout {
container-type: inline-size;
container-name: layout;
}
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.main-content {
container-type: inline-size;
container-name: main;
}
/* Query a specific container by name */
@container sidebar (min-width: 300px) {
.sidebar-widget {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
@container main (min-width: 700px) {
.article-card {
flex-direction: row;
}
}
There is also a shorthand container property that sets both name and type:
/* Shorthand: container: <name> / <type> */
.sidebar {
container: sidebar / inline-size;
}
/* Equivalent to: */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
Without a name, @container queries match the nearest ancestor that has container-type set. Naming is only necessary when you need to skip over a closer container to query one further up the tree.
The @container Syntax
The @container at-rule works like @media, but queries the container instead of the viewport:
/* Basic size query */
@container (min-width: 500px) {
.component { /* styles */ }
}
/* Named container query */
@container card-area (min-width: 400px) {
.card { /* styles */ }
}
/* Range syntax (same as media queries level 4) */
@container (width >= 600px) {
.component { /* styles */ }
}
@container (300px <= width < 600px) {
.component { /* medium layout */ }
}
/* Combining conditions */
@container (min-width: 500px) and (min-height: 300px) {
.panel { /* both conditions must be true */ }
}
Size Queries: inline-size, block-size, width, height
Size queries are the most common type of container query. You can query these dimensions:
/* Width-based queries (require container-type: inline-size or size) */
@container (min-width: 400px) { ... }
@container (max-width: 599px) { ... }
@container (width >= 400px) { ... }
@container (400px <= width < 800px) { ... }
/* Logical equivalents */
@container (min-inline-size: 400px) { ... }
@container (inline-size >= 400px) { ... }
/* Height-based queries (require container-type: size) */
@container (min-height: 300px) { ... }
@container (height >= 300px) { ... }
@container (min-block-size: 300px) { ... }
/* Aspect ratio */
@container (aspect-ratio > 1) {
/* Container is wider than it is tall */
}
/* Orientation */
@container (orientation: landscape) {
/* Container width exceeds its height */
}
In practice, you will use width-based queries almost exclusively. Height queries require container-type: size, which in turn requires an explicit height on the container. Reserve height queries for fixed-dimension contexts like dashboard widgets or split-pane editors.
Style Queries: @container style()
Style queries let you apply styles based on the computed value of CSS custom properties on a container. Instead of querying dimensions, you query the container's style:
/* Define a custom property on the container */
.card-wrapper {
container-type: inline-size;
--card-variant: default;
}
.card-wrapper.featured {
--card-variant: featured;
}
.card-wrapper.compact {
--card-variant: compact;
}
/* Query the custom property value */
@container style(--card-variant: featured) {
.card {
border: 2px solid var(--primary);
background: rgba(59, 130, 246, 0.05);
}
.card-title {
font-size: 1.5rem;
color: var(--primary);
}
}
@container style(--card-variant: compact) {
.card {
flex-direction: row;
padding: 0.5rem;
gap: 0.75rem;
}
.card-image {
width: 60px;
height: 60px;
border-radius: 50%;
}
}
Style queries are powerful for theming and variant systems. You set a custom property on a container, and all descendants respond automatically. No extra classes on every child element. Note that style queries currently work in Chrome 111+ and Edge 111+; Firefox and Safari support is still in development as of early 2026.
Container Query Units
Container queries introduce six new length units relative to the container's dimensions:
/* Container query units reference:
cqw = 1% of container width
cqh = 1% of container height
cqi = 1% of container inline size
cqb = 1% of container block size
cqmin = smaller of cqi and cqb
cqmax = larger of cqi and cqb */
/* Fluid typography relative to container width */
.card-title {
font-size: clamp(1rem, 4cqi, 2rem);
}
/* Spacing that scales with the container */
.card-body {
padding: clamp(0.75rem, 3cqi, 2rem);
}
/* Image sizing relative to container */
.card-image {
height: clamp(120px, 30cqi, 300px);
}
/* Responsive icon size */
.widget-icon {
width: clamp(24px, 8cqi, 48px);
height: clamp(24px, 8cqi, 48px);
}
Container query units are especially useful inside clamp() for fluid scaling. They work like viewport units (vw, vh) but are scoped to the container, making them perfect for component-level fluid design. They resolve against the nearest ancestor with container-type set.
Nesting Containers
You can nest containers freely. Each @container query resolves against the nearest matching container ancestor:
/* Outer container */
.page-layout {
container: page / inline-size;
display: grid;
grid-template-columns: 1fr;
}
/* Inner container */
.content-area {
container: content / inline-size;
}
/* This queries the nearest container (content-area) */
@container (min-width: 500px) {
.article {
columns: 2;
}
}
/* This queries the named outer container */
@container page (min-width: 1024px) {
.page-layout {
grid-template-columns: 280px 1fr;
}
}
/* This queries the named inner container */
@container content (min-width: 700px) {
.article {
columns: 3;
column-gap: 2rem;
}
}
When nesting, use named containers to be explicit about which container you are querying. Unnamed queries match the closest ancestor container, which can lead to unexpected results if you add a new container in between.
Real-World Patterns
Pattern 1: Responsive Card Component
/* The wrapper is the container */
.card-wrapper {
container-type: inline-size;
}
/* Default: vertical stack (narrow contexts) */
.card {
display: flex;
flex-direction: column;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
overflow: hidden;
}
.card-img {
width: 100%;
height: 180px;
object-fit: cover;
}
.card-body { padding: 1rem; }
.card-title { font-size: 1.1rem; font-weight: 600; }
/* Medium: horizontal layout */
@container (min-width: 450px) {
.card { flex-direction: row; }
.card-img {
width: 200px;
height: auto;
min-height: 160px;
}
}
/* Wide: featured style with larger text */
@container (min-width: 700px) {
.card-img { width: 320px; }
.card-title { font-size: 1.5rem; }
.card-body { padding: 1.5rem; }
}
Pattern 2: Adaptive Sidebar Navigation
.sidebar {
container: sidebar / inline-size;
}
/* Narrow sidebar: icon-only nav */
.nav-item {
display: flex;
align-items: center;
justify-content: center;
padding: 0.75rem;
border-radius: 6px;
}
.nav-label { display: none; }
.nav-icon {
width: 24px;
height: 24px;
flex-shrink: 0;
}
/* Wide sidebar: icons + labels */
@container sidebar (min-width: 200px) {
.nav-item {
justify-content: flex-start;
gap: 0.75rem;
padding: 0.75rem 1rem;
}
.nav-label {
display: block;
white-space: nowrap;
}
}
/* Extra wide: add descriptions */
@container sidebar (min-width: 300px) {
.nav-description {
display: block;
font-size: 0.8rem;
color: var(--text-muted);
}
}
Pattern 3: Dashboard Widget
.widget-wrapper {
container: widget / inline-size;
}
.widget {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 1rem;
}
.widget-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.widget-stat {
font-size: clamp(1.5rem, 6cqi, 3rem);
font-weight: 700;
}
/* Small widget: stack stat and chart */
.widget-content {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Medium widget: side by side */
@container widget (min-width: 400px) {
.widget-content {
flex-direction: row;
align-items: center;
}
.widget-chart {
flex: 1;
min-height: 120px;
}
}
/* Large widget: more detail */
@container widget (min-width: 600px) {
.widget { padding: 1.5rem; }
.widget-details { display: block; }
.widget-table { display: table; }
}
Pattern 4: Responsive Navigation Bar
.nav-container {
container: topnav / inline-size;
}
/* Narrow: hamburger menu */
.nav-links { display: none; }
.menu-toggle { display: flex; }
/* Medium: priority links visible */
@container topnav (min-width: 500px) {
.menu-toggle { display: none; }
.nav-links {
display: flex;
gap: 0.5rem;
}
.nav-links .secondary { display: none; }
}
/* Wide: all links + CTA visible */
@container topnav (min-width: 800px) {
.nav-links .secondary { display: flex; }
.nav-cta {
display: inline-flex;
margin-left: auto;
}
}
Container Queries vs Media Queries: When to Use Each
Container queries do not replace media queries. Each serves a different purpose, and the best responsive designs use both.
Use media queries for:
- Page-level layout decisions (show/hide sidebar, change grid columns)
- User preference detection (dark mode, reduced motion, contrast)
- Print stylesheets
- Viewport-relative typography and spacing
- Device capability detection (hover, pointer, resolution)
Use container queries for:
- Reusable components that appear in multiple contexts
- Cards, widgets, and panels that need to adapt to available space
- Design system components meant to work in any layout
- Sidebar content that has a different width from the main area
- Components inside resizable panels or dynamic grids
/* Typical approach: combine both */
/* Media query: page-level layout */
@media (min-width: 1024px) {
.page {
display: grid;
grid-template-columns: 300px 1fr;
}
.sidebar { display: block; }
}
/* Container query: component-level adaptation */
.card-wrapper { container-type: inline-size; }
@container (min-width: 450px) {
.card { flex-direction: row; }
}
/* Media query: user preferences */
@media (prefers-color-scheme: dark) {
:root { --bg: #0f1117; }
}
@media (prefers-reduced-motion: reduce) {
* { transition-duration: 0.01ms !important; }
}
Browser Support and Progressive Enhancement
As of February 2026, container query support is excellent:
- Size queries (
@containerwith width/height): Chrome 105+, Firefox 110+, Safari 16+, Edge 105+. Over 95% global support. - Container query units (cqw, cqi, etc.): Same support as size queries.
- Style queries (
@container style()): Chrome 111+, Edge 111+. Firefox and Safari still in development.
For progressive enhancement, use @supports to detect container query support:
/* Fallback: media query for older browsers */
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
/* Enhancement: container query for modern browsers */
@supports (container-type: inline-size) {
.card-wrapper {
container-type: inline-size;
}
/* Override the media query with container query */
.card {
flex-direction: column; /* Reset to default */
}
@container (min-width: 450px) {
.card {
flex-direction: row;
}
}
}
This approach ensures older browsers get a reasonable media-query-based responsive layout, while modern browsers get the superior container-query-based one.
Performance Considerations
Container queries are well optimized in modern browsers, but there are a few things to keep in mind:
- Containment is required by design. When you set
container-type: inline-size, the browser establishes layout containment on that element. This is actually a performance feature: the browser knows that changes inside the container cannot affect elements outside it, which limits the scope of layout recalculations. - Avoid deeply nested containers. Each container boundary creates a containment context that the browser must evaluate. A few levels of nesting is fine, but wrapping every element in a container creates unnecessary overhead.
- Do not make everything a container. Only add
container-typeto elements that have children that need to respond to the container's size. A good rule: if the element's children do not have any@containerqueries targeting it, it does not need to be a container. - Container query units are cheap. They resolve at style computation time and do not trigger extra layout passes. Use them freely for fluid typography and spacing.
/* GOOD: Container on the wrapper, not every element */
.widget-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
}
.widget-wrapper {
container-type: inline-size;
}
/* BAD: Unnecessary containers everywhere */
.widget-grid { container-type: inline-size; } /* Not needed */
.widget-wrapper { container-type: inline-size; } /* This one is needed */
.widget { container-type: inline-size; } /* Not needed */
.widget-body { container-type: inline-size; } /* Not needed */
Migration Strategy from Media Queries
You do not need to rewrite your entire CSS to adopt container queries. Here is a practical step-by-step migration strategy:
Step 1: Identify candidates. Look for components that appear in multiple layout contexts. Cards, widgets, navigation components, and form elements that live in both sidebars and main content areas are the best candidates.
Step 2: Add containers to wrappers. Add container-type: inline-size to the parent wrappers of those components. This has no visual effect on its own.
Step 3: Convert media queries to container queries. For each candidate component, change @media (min-width: ...) to @container (min-width: ...). Adjust the breakpoint values — container widths are typically smaller than viewport widths.
Step 4: Keep media queries for page layout. Continue using @media for page-level grid changes, sidebar visibility, and user preferences.
/* BEFORE: Everything uses media queries */
.card { flex-direction: column; }
@media (min-width: 768px) {
.card { flex-direction: row; }
.sidebar { display: block; width: 300px; }
}
/* AFTER: Mixed approach */
/* Page layout: still media queries */
@media (min-width: 1024px) {
.page { grid-template-columns: 300px 1fr; }
.sidebar { display: block; }
}
/* Component layout: container queries */
.card-wrapper { container-type: inline-size; }
.card { flex-direction: column; }
@container (min-width: 450px) {
.card { flex-direction: row; }
}
Step 5: Test in every context. Resize the browser, toggle sidebars, and check every place the component appears. Container queries change the component's behavior in ways that viewport-based testing alone will not reveal.
Frequently Asked Questions
What is the difference between container queries and media queries?
Media queries respond to the viewport (browser window) size, while container queries respond to the size of a parent container element. Media queries use @media and are ideal for page-level layout changes like switching column counts. Container queries use @container and let components adapt based on the space they actually occupy. A card in a 300px sidebar behaves differently from the same card in a 900px content area, automatically. Use media queries for page layout and user preferences; use container queries for reusable components.
What is the difference between container-type inline-size and size?
container-type: inline-size enables containment on the inline axis only (width in horizontal writing modes). This is the default choice for almost all use cases. container-type: size enables containment on both width and height, but requires the container to have an explicit height set. Without one, the container may collapse. Only use size when you need height-based container queries, such as for fixed-dimension dashboard widgets or vertically scrolling panels.
What are container query units like cqw and cqi?
Container query units are relative lengths based on the nearest container ancestor. cqw = 1% of container width, cqh = 1% of container height, cqi = 1% of container inline size, cqb = 1% of container block size, cqmin = smaller of cqi and cqb, cqmax = larger. They work like viewport units but scoped to the container, making them ideal for fluid typography: font-size: clamp(1rem, 4cqi, 2rem) scales text based on available container space.
Do CSS container queries work in all browsers?
Size container queries are supported in Chrome 105+, Firefox 110+, Safari 16+, and Edge 105+ — over 95% global coverage in 2026. Style queries (@container style()) are more limited: Chrome 111+ and Edge 111+ only, with Firefox and Safari still developing support. For older browsers, use @supports (container-type: inline-size) to provide a media-query fallback alongside container query enhancements.
How do I migrate from media queries to container queries?
Migrate incrementally. Identify components that appear in multiple layout contexts (cards, widgets, navigation). Add container-type: inline-size to their parent wrappers. Convert each component's @media rules to @container rules, adjusting breakpoint values since container widths are smaller than viewport widths. Keep media queries for page-level layout and user preferences. Use @supports to provide fallbacks for older browsers. Test in every context where the component appears.
Summary
Container queries are the most significant advancement in CSS layout since Flexbox and Grid. They solve a problem that media queries structurally cannot: making components responsive to their own context rather than the viewport. Here are the key takeaways:
- Use
container-type: inline-sizeon wrapper elements to create containment contexts. This is the only value you need in most cases. - Use
@containerqueries for components that appear in multiple layout contexts: cards, widgets, navigation, and any reusable UI element. - Keep
@mediaqueries for page-level layout, user preferences (dark mode, reduced motion), and device capabilities (hover, pointer). - Use container query units (
cqi,cqw) withclamp()for fluid typography and spacing inside components. - Name your containers with
container-namewhen nesting to be explicit about which container you are querying. - Use
@supportsfor progressive enhancement so older browsers fall back to media queries gracefully. - Migrate incrementally — start with your most reused components and work outward.
For more on responsive CSS layout, read our CSS Grid Complete Guide, CSS Flexbox Complete Guide, and CSS Media Queries Complete Guide. For quick property references, keep our CSS Flexbox Cheat Sheet bookmarked.