Container Queries
The Problem
Media queries respond to the viewport width, not the width of the component's container. When a component is placed in a sidebar, a modal, or any constrained layout, viewport-based media queries cannot adapt the component's layout to its actual available space. AI agents almost always reach for @media queries for component-level responsiveness, ignoring container queries entirely.
The Solution
CSS Container Queries (@container) allow components to respond to the size of their parent container rather than the viewport. This makes components truly reusable across different layout contexts. Container queries are Baseline 2023 and supported in all modern browsers.
Setting Up a Container
A parent element must be declared as a containment context using container-type. The most common value is inline-size, which enables queries based on the container's inline (horizontal) dimension.
.card-wrapper {
container-type: inline-size;
}
Querying the Container
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
Basic Container Query
The iframe in this demo acts as the container boundary. Use the viewport buttons to see the card layout adapt to the container width.
Named Containers
When containers are nested, @container queries match the nearest ancestor with container-type set. To target a specific container, use container-name and reference it in the query.
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.main-content {
container-type: inline-size;
container-name: main;
}
/* Only responds to the sidebar container */
@container sidebar (max-width: 300px) {
.nav-list {
flex-direction: column;
}
}
The shorthand container property combines both:
.sidebar {
container: sidebar / inline-size;
}
Container Query Units
Container query units are relative to the dimensions of the query container. These are useful for fluid sizing within a component.
cqw— 1% of the container's widthcqh— 1% of the container's heightcqi— 1% of the container's inline sizecqb— 1% of the container's block sizecqmin— the smaller ofcqiorcqbcqmax— the larger ofcqiorcqb
.card-container {
container-type: inline-size;
}
.card__title {
/* 5% of the container's inline size, clamped */
font-size: clamp(1rem, 5cqi, 2rem);
}
.card__body {
/* Padding relative to container width */
padding: 2cqi;
}
Card Component Adapting to Container Width
A common real-world use case is a card component that works in any layout context: a narrow sidebar, a medium-width grid column, or a full-width main area.
Container Queries vs. Media Queries
The key difference: media queries respond to the viewport, while container queries respond to the parent container. This demo places the same component in two different-width containers on the same page. The media query version looks identical in both because the viewport hasn't changed. The container query version adapts to each container independently.
In the demo above, the @container cards adapt independently: the card in the narrow container stacks vertically while the card in the wide container goes horizontal. The @media cards both go horizontal because the viewport (the iframe) is wider than 300px — neither card knows how wide its actual container is.
Code Examples
Responsive Card Component
.card-container {
container-type: inline-size;
container-name: card;
}
/* Base: stacked layout */
.card {
display: flex;
flex-direction: column;
}
.card__image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
/* When container is wide enough: horizontal layout */
@container card (min-width: 500px) {
.card {
flex-direction: row;
}
.card__image {
width: 200px;
aspect-ratio: 1;
}
}
/* When container is very wide: add extra spacing */
@container card (min-width: 800px) {
.card {
gap: 2rem;
padding: 2rem;
}
.card__image {
width: 300px;
}
}
Navigation That Adapts to Its Container
.nav-wrapper {
container-type: inline-size;
container-name: nav;
}
.nav-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
list-style: none;
padding: 0;
margin: 0;
}
/* Horizontal layout when container allows */
@container nav (min-width: 600px) {
.nav-list {
flex-direction: row;
gap: 1rem;
}
}
Combining Container Queries with Container Query Units
.widget-wrapper {
container: widget / inline-size;
}
.widget__title {
font-size: clamp(1rem, 5cqi, 2rem);
}
.widget__body {
padding: clamp(0.5rem, 3cqi, 1.5rem);
}
@container widget (min-width: 400px) {
.widget {
display: grid;
grid-template-columns: auto 1fr;
gap: 1rem;
}
}
Common AI Mistakes
- Using media queries for component layouts: AI agents default to
@mediaqueries even when the component needs to adapt to its container, not the viewport. - Forgetting
container-type: Writing@containerrules without settingcontainer-typeon the parent element. The container must be explicitly declared. - Using
container-type: sizeunnecessarily: Height-based containment (size) can cause layout issues. Useinline-sizefor the vast majority of cases. - Not naming nested containers: When containers are nested, omitting
container-nameleads to ambiguity —@containerqueries match the nearest ancestor container. Name containers when nesting to target specific ancestors. - Querying the element itself: The
@containerquery targets the nearest ancestor withcontainer-typeset, not the element you are styling. The container and the styled element must be different elements.
When to Use
- Component-level responsiveness: Any reusable component that may appear in different layout widths (cards, navigation, form groups).
- Sidebar vs. main content: When the same component appears in both wide and narrow contexts on the same page.
- Design system components: Components built for reuse across different applications and layouts.
- Not for page-level layout: Continue using
@mediaqueries for macro layout concerns like switching between single-column and multi-column page layouts.