CSS Container Queries
Style components based on their own container size rather than the viewport, enabling truly portable responsive components.
Overview
Key Points
- •container-type: inline-size enables width-based queries on a parent element
- •container-name gives the container an identifier so nested containers can be targeted specifically
- •Container queries use the same range syntax as media queries: (width > 400px)
- •cqi and cqb units scale relative to the container, not the viewport
- •An element cannot query itself, only its ancestor containers
- •Multiple container names can be assigned: container-name: card sidebar
- •Style queries (@container style()) can query custom property values on a container
- •Supported in Chrome 105+, Firefox 110+, Safari 16+ covering all modern browsers
Examples
Basic container query for a card component
/* The wrapper becomes a container */
.card-wrapper {
container-type: inline-size;
}
/* Default: stacked layout */
.card {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
border: 1px solid #e5e7eb;
border-radius: 12px;
background: white;
}
/* Wide container: switch to row layout */
@container (min-width: 480px) {
.card {
flex-direction: row;
align-items: center;
}
.card-image {
width: 140px;
flex-shrink: 0;
}
}The card switches between stacked and side-by-side based on the card wrapper width, not the viewport.
Named containers for precise targeting
/* Name each container */
.sidebar {
container: sidebar / inline-size;
}
.main {
container: main / inline-size;
}
/* Same component, different behaviour per context */
@container sidebar (max-width: 240px) {
.widget {
font-size: 0.8rem;
padding: 8px;
}
}
@container main (min-width: 600px) {
.widget {
font-size: 1rem;
padding: 20px;
display: grid;
grid-template-columns: 1fr 1fr;
}
}Named containers let the same component adapt differently depending on which container it lives in.
Container query units (cqi)
.hero-wrapper {
container-type: inline-size;
}
.hero-title {
/* Font scales with the container width */
font-size: clamp(1.5rem, 8cqi, 4rem);
line-height: 1.1;
}
.hero-body {
font-size: clamp(0.9rem, 3cqi, 1.25rem);
}
/* Column count based on container */
@container (min-width: 600px) {
.feature-grid {
grid-template-columns: repeat(3, 1fr);
}
}8cqi means 8% of the container inline size, making typography scale with component width rather than viewport width.
Style queries (querying custom property values)
/* Mark the container */
.theme-wrapper {
container-type: normal;
}
/* Query the custom property value */
@container style(--theme: dark) {
.card {
background: #1e293b;
color: #f1f5f9;
border-color: #334155;
}
}
@container style(--theme: accent) {
.card {
background: #eef2ff;
border-color: #6366f1;
color: #4338ca;
}
}
/* Apply theme by setting the variable on the wrapper */
.dark-section {
--theme: dark;
}Common Mistakes
- Querying the container on the container element itself (an element cannot query its own container)
- Forgetting to set container-type on the parent, so @container queries match the viewport instead
- Using container queries when a media query is actually more appropriate (global layout vs component layout)
- Nesting @container without naming, causing ambiguity about which ancestor is being queried
Note
Use container queries for component-level responsiveness (cards, widgets, sidebars) and media queries for page-level layout (header, grid columns, font scale). Combining both gives you full responsive control.
Exercises
Write CSS to style the HTML scaffold. The preview updates live — when it looks right, mark as done.
Completed: 0 / 3 exercises
Knowledge Check
Question 1 of 4