CSS Container Queries
Media queries ask: how wide is the viewport? Container queries ask: how wide is this component's slot? That shift is what makes reusable UI -- cards, widgets, sidebars -- adapt to where they are placed, not only to the device.
This guide goes deeper than the short intro in the Modern CSS Features chapter, with interactive demos and a clear comparison to media queries and device-width thinking.
The problem container queries solve
A card that looks fine in a wide main column can break when you drop it into a narrow sidebar. With
viewport media queries alone, both slots see the same @media (min-width: 768px) -- so the sidebar
card still gets the "desktop" layout even though it only has 280px of space.
Container queries fix that. The card styles itself based on its container, so the same component works in both places without extra modifier classes or JavaScript measuring.
Core API
Define a containment context
A query only works against an ancestor that is a container. The usual choice is inline-size
(track width / inline size):
.card-slot {
container-type: inline-size;
container-name: card;
}
/* Shorthand: name + type */
.card-slot {
container: card / inline-size;
}
| Property / value | Meaning |
|---|---|
container-type: inline-size | Query against inline size (width in horizontal writing modes). Preferred default. |
container-type: size | Query against both inline and block size. Needs a definite block size; use sparingly. |
container-type: normal | No size containment for queries (default). |
container-name | Optional name so @container can target a specific ancestor. |
Query the container
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
}
/* Nearest size container, no name required */
@container (max-width: 399px) {
.card {
display: block;
}
}
Named queries are useful when containers nest. Unnamed queries use the nearest ancestor with a
relevant container-type.
Container query units
Each cq* unit resolves against an eligible query container for its axis (for example cqw /
cqi need an inline-size container). If no eligible container exists for that axis, the unit falls
back to the matching small viewport unit (svw, svh, svi, svb) -- the viewport is the
last-resort reference, not the first.
| Unit | Relative to |
|---|---|
cqw | 1% of container width |
cqh | 1% of container height |
cqi | 1% of container inline size |
cqb | 1% of container block size |
cqmin / cqmax | Smaller / larger of cqi and cqb |
.card-title {
font-size: clamp(1rem, 4cqi, 1.5rem);
}
Style queries (brief)
Besides size, you can query custom properties on a container:
.card-slot {
container-name: card;
--density: comfortable;
}
@container card style(--density: compact) {
.card {
padding: 0.5rem;
}
}
Browser support for style queries is newer than size queries. Prefer size queries for core layout; treat style queries as progressive enhancement.
Interactive demos
Resize the panels below. CSS @container rules select the layout. React only manages the demo
controls (slider, labels) and synchronizes manual panel resizing from the measured panel width so
the controls stay in sync.
Card that adapts to its container
Drag the right edge of the panel, or use the slider.
Component
Same card, different slot sizes
This layout responds to the panel width -- not the browser viewport. Drop the same component in a sidebar or a main column and it still fits.
Same slot width, different query type
Use the slider to resize the shared slot. The media-query widget only changes when the viewport crosses 768px. The container-query widget changes with the slot.
@media (viewport)
Media query state
Viewport-driven
Shrinking this panel does nothing. Open DevTools device mode or resize the browser window to see it change.
@container (slot)
Container query state
Container-driven
This widget flips layout around 320px of panel width -- useful for sidebars, cards, and reusable components.
Container queries vs media queries vs device width
| Approach | Responds to | Best for | Weakness |
|---|---|---|---|
| Device-width mindset | Assumed phone / tablet / desktop | Quick prototypes, marketing copy | Real devices blur; foldables and split-screen break the model |
Media queries (@media) | Viewport (and preference features) | Page chrome, global typography, prefers-*, print | Same breakpoint for every slot; bad for reusable components |
Container queries (@container) | A specific ancestor's size | Cards, widgets, modular layouts | Needs a defined container; different mental model and a few containment gotchas |
Pros of container queries
- Component-owned responsiveness -- a card adapts in a sidebar and in the main column without viewport hacks.
- Fewer layout bugs in complex pages -- dashboards, multi-column CMS layouts, and design systems stop fighting one global breakpoint list.
- Fluid type and spacing per slot --
cqi/cqwscale with the component, not the whole window. - Cleaner APIs for design systems -- consumers place a component; the component owns its breakpoints.
Cons of container queries
- You must opt in -- forgetting
container-typemeans queries never match. - Containment side effects -- size containment can affect how percentages and overflowing content
behave;
inline-sizeis usually safer thansize. - Not a full media-query replacement -- dark mode, reduced motion, hover capability, and print
still belong in
@media. - Nested naming care -- deep trees need clear
container-names or queries attach to the wrong ancestor.
Pros of media queries (and when device width still helps)
- Page-level decisions -- hide a global nav pattern, change the whole grid, or adjust root font size.
- User preference features --
prefers-color-scheme,prefers-reduced-motion,hover,orientation. - Simple mental model for whole-page work -- one viewport, one set of breakpoints.
- Device-width framing is still useful for content strategy ("what does a small screen need first?") even if you should not hard-code "iPhone width" into CSS.
Cons of media queries for components
- Viewport ≠ available space -- split view, sidebars, and embedded widgets lie to viewport-only CSS.
- Modifier class explosion --
.card--compact,.card--sidebarappear when the same component must look different in different slots. - Device breakpoints age poorly -- yesterday's "tablet" width is today's desktop split-screen.
Practical rule of thumb
- Use media queries for the page shell and user preferences.
- Use container queries for reusable pieces that can appear in multiple widths.
- Use device-width language in product discussions; implement with fluid layouts, container queries, and a short list of viewport breakpoints -- not a catalogue of device models.
Practical patterns
Card in sidebar vs main column
.card-slot {
container: card / inline-size;
}
.card {
display: grid;
gap: 0.75rem;
}
@container card (min-width: 360px) {
.card {
grid-template-columns: 140px 1fr;
align-items: center;
}
}
Place .card-slot around the card in both the sidebar and the main column. No extra BEM modifiers.
Dashboard widgets
Give each widget shell container-type: inline-size. Dense metrics can stack in a narrow column and
expand to a horizontal strip when the widget is stretched -- independent of other widgets on the
same page.
Progressive enhancement
.card {
display: block;
}
@supports (container-type: inline-size) {
.card-slot {
container: card / inline-size;
}
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
}
}
Unsupported browsers keep the stacked layout. Supported browsers unlock the richer arrangement.
Gotchas
- Queries look at an ancestor, not the element itself. Put
container-typeon a wrapper around the component you style inside@container. sizevsinline-size. Preferinline-sizeunless you truly need height-based queries and can give the container a definite block size.- Percentage heights and overflow can surprise you under stronger containment. If something
collapses oddly, check whether
container-type: sizeis the culprit. - Nested containers. Name them (
container-name/containershorthand) when more than one level can match. - Fallbacks still matter. Pair with a sensible default layout and
@supportswhen you support older browsers. - Do not delete all media queries. Accessibility and preference media features are not container problems.
Browser support
Size-based container queries are widely supported in current evergreen browsers. For production
check Can I use: CSS Container Queries or MDN. Use
@supports (container-type: inline-size) when you need an explicit feature gate.
Related reading
- Responsive Design -- viewport meta tag, media queries, mobile-first breakpoints
- Modern CSS Features -- nesting,
:has(), short container-query intro,@supports - CSS Grid --
auto-fit/minmax()layouts that pair well with containers