Container queries vs media queries: when to use which
Media queries react to the screen, container queries to the space a component actually has. Syntax, cqi units, support, Tailwind v4 and a card example.
Media queries respond to the viewport, the size of the browser window. Container queries respond to the size of an element's container, the slot a component actually sits in. Use media queries for the page layout and device features, and container queries for components like cards that show up in wide and narrow places.
What each one responds to
| Media query | Container query | |
|---|---|---|
| Asks | How big is the screen (viewport)? | How big is my container? |
| Can also check | Hover, pointer type, orientation, dark mode, reduced motion | Only size (plus newer style queries) |
| Setup needed | None | Mark an ancestor with container-type |
| Best for | Page layout: columns, sidebar, navigation | Reusable components: cards, tiles, widgets |
The difference matters because a component doesn't live on "a phone" or "a laptop". It lives in a slot. On a MacBook Air 13″ (1470px wide), a card in a three-column grid is about 373px wide, practically the same as the full-width card on a 402px iPhone 17 Pro (370px after padding). A media query sees 1470 and says "desktop". A container query sees 373 and says "narrow".
The syntax
Declare a container
.card-slot { container-type: inline-size; }
inline-size makes the element a query container for its inline size, which is its width in horizontal text. size adds height too, but then the element can't take its height from its content. Use inline-size almost every time (MDN: container-type).
Write the query
@container (width >= 32rem) {
.card { grid-template-columns: 2fr 3fr; }
}
The rules inside apply when the nearest ancestor container is at least 32rem (512px) wide. (min-width: 32rem) works too. Two rules that trip people up:
- An element can't query itself. The query styles the container's descendants, so put
container-typeon a wrapper and style the card inside it (MDN: container queries). - The container stops sizing to its content. With
inline-sizecontainment, the element's width is worked out as if it were empty. Normal blocks and grid items are sized by their parent anyway, so nothing changes. Aninline-block, a float or a flex item with no width set can shrink to nothing.
Name containers when they nest
A query targets the nearest container. When containers sit inside each other, name them:
.sidebar { container: sidebar / inline-size; } /* name / type */
@container sidebar (width >= 20rem) {
.widget { display: flex; }
}
Container query units
Container queries come with units that work like vw, but measure the container instead of the screen:
| Unit | Means |
|---|---|
cqi |
1% of the container's inline size (its width, in horizontal text) |
cqw / cqh |
1% of the container's width / height |
cqb |
1% of the container's block size |
cqmin / cqmax |
the smaller / larger of cqi and cqb |
If there's no container above an element, these fall back to the small viewport units (svw, svh and friends). The most useful one is cqi for headings that scale with their card:
.card h3 { font-size: clamp(1.125rem, 1rem + 1.5cqi, 1.75rem); }
That's the same fluid-type recipe as in responsive font sizes with clamp(), with the rem part kept so zoom still works.
Browser support
Size container queries and the container query units have worked in all major browsers since February 2023: Chrome and Edge 105, Safari 16 and Firefox 110 (web.dev). MDN marks container-type as Baseline widely available.
If a very old browser meets an @container block it doesn't understand, it skips it. That's another reason to write the narrow layout as the default and add the wide one in the query: the fallback is then a working, stacked card. More on that in mobile-first CSS.
Style queries (@container style(...)) and scroll-state queries are separate, newer features with their own support tables. This guide only covers size queries.
Example: one card, from a 360px phone to a 1470px laptop
Here's a product card that should stack (image on top) when its slot is narrow and go side by side when there's room. It's used twice: a featured card across the full width, and normal cards in a grid.
<main class="page">
<ul class="cards">
<li class="card-slot featured">
<article class="card">
<img src="featured.jpg" alt="…" width="1200" height="800">
<div>
<h3>Featured product</h3>
<p>One or two lines of description.</p>
</div>
</article>
</li>
<li class="card-slot">
<article class="card">…</article>
</li>
<!-- more cards -->
</ul>
</main>
The page layout: media queries
The number of columns depends on the screen, so that's a media query's job:
body { margin: 0; }
.page {
box-sizing: border-box;
max-width: 75rem; /* 1200px */
margin-inline: auto;
padding-inline: 1rem; /* 16px each side */
}
.cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.5rem; }
@media (min-width: 40rem) { /* 640px: two columns */
.cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.featured { grid-column: 1 / -1; }
}
@media (min-width: 64rem) { /* 1024px: three columns */
.cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
The card: a container query
The card's layout depends on its slot, so that's a container query's job:
.card-slot { container-type: inline-size; }
.card { display: grid; gap: 0.75rem; }
.card img {
display: block;
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card h3 { margin: 0; font-size: clamp(1.125rem, 1rem + 1.5cqi, 1.75rem); line-height: 1.2; }
@container (width >= 32rem) { /* slot at least 512px wide */
.card { grid-template-columns: 2fr 3fr; align-items: start; gap: 1.25rem; }
.card img { aspect-ratio: 1; }
}
What each screen gets
| Screen | Page width | Card slot | Card layout | h3 size |
|---|---|---|---|---|
| Galaxy S25 | 360 | 328 | stacked | 20.9px |
| iPhone Duo, outer screen | 382 | 350 | stacked | 21.3px |
| iPad Air 11″, grid card | 820 | 382 | stacked | 21.7px |
| iPad Air 11″, featured card | 820 | 788 | side by side | 27.8px |
| MacBook Air 13″, grid card | 1470 | 373 | stacked | 21.6px |
| MacBook Air 13″, featured card | 1470 | 1168 | side by side | 28px (max) |
The iPhone Duo's outer screen is 466px wide, but iOS 27 keeps an 84px strip on its right edge, so pages get 382px. After 16px of padding on each side, the card slot is 350px.
The MacBook sums: the page stops at 1200px, minus 32px of padding is 1168px, minus two 24px gaps is 1120px, divided by three columns is 373.33px per card. The iPad Air: 820 − 32 = 788px, minus one gap = 764px, two columns of 382px. On all of these screens, grid cards land between 328 and 382px wide, so they all stack. Only the featured card, at 788px or wider, has room to go side by side.
The media-query version, and why it misbehaves
The usual way to write the card is "side by side from tablet up":
@media (min-width: 48rem) { /* 768px */
.card { grid-template-columns: 2fr 3fr; }
}
On the MacBook, every 373px grid card now goes side by side: 361px after the 12px gap, split 2:3, leaves a 145px image and a 217px text column. That's a narrower text column than the same card gets on a phone. The iPad Air's 382px cards get the same squeeze, while the iPad mini at 744px stays under 768 and gets stacked cards. Two iPads, two layouts, neither decided by the space the card actually has.
Fixing that with media queries means copying the grid's column maths into the card's CSS, and redoing it whenever the card moves. The container query version needs no changes.
When to use which
Use a media query when the question is "how big is the screen, or what kind of device is this?":
- Page layout: how many columns, whether the sidebar shows, when the nav collapses into a menu (mobile menus that work).
- Device and user features:
(hover: hover),(pointer: coarse),(prefers-reduced-motion: reduce),(prefers-color-scheme: dark). - Phones on their side, which need a
max-heightquery (landscape on phones). - Anything on
htmlorbody, which have no container above them.
Use a container query when the question is "how much room do I have?":
- Cards, product tiles and list items that appear in grids, carousels and sidebars.
- Widgets and dashboard panels that users can resize or rearrange.
- Components in a design system or a CMS, where you don't know which page they'll land on.
Most sites need both: media queries for the frame, container queries for the pieces inside it. Where to put the page-level breakpoints is covered in responsive breakpoints in 2026.
Container queries in Tailwind
Tailwind v4.0 (January 2025) made container queries built-in (Tailwind v4.0 release). Tailwind v3 needs the @tailwindcss/container-queries plugin. The same card in v4:
<ul class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<li class="@container sm:col-span-full">
<article class="grid gap-3 @lg:grid-cols-[2fr_3fr] @lg:items-start @lg:gap-5">
<img class="aspect-video w-full object-cover @lg:aspect-square"
src="featured.jpg" alt="…" width="1200" height="800">
<div>
<h3 class="text-[clamp(1.125rem,1rem+1.5cqi,1.75rem)] leading-tight font-semibold">Featured product</h3>
<p>One or two lines of description.</p>
</div>
</article>
</li>
<li class="@container">…</li>
</ul>
@containersetscontainer-type: inline-sizeon the element.@lg:means "container at least 32rem (512px)". It is not the same aslg:, which means "viewport at least 1024px". The container sizes run from@3xs(16rem, 256px) to@7xl(80rem, 1280px):@smis 24rem (384px) and@mdis 28rem (448px) (Tailwind: container queries).@max-md:applies below a container size, and you can stack them for ranges:@sm:@max-md:flex-col.- One-off sizes:
@min-[30rem]:flex-row. - Named containers:
@container/cardon the parent,@lg/card:grid-cols-2on the child. - Container units work in arbitrary values, like the
cqiheading above orw-[50cqw].
The grid still uses viewport variants (sm:grid-cols-2 lg:grid-cols-3): viewport for the grid, container for the card. Those are mapped onto real devices in Tailwind breakpoints on real devices.
Does your site fit?
Paste your address (or localhost:3000). doesitfit shows it on real screen sizes at once and tells you, bluntly, where it spills.
Free, no sign-up. Or ask Claude, ChatGPT or Codex to check it.
Container queries are exactly the kind of thing that looks right on the one screen you built it on. In doesitfit you can load a 360px Galaxy, a 402px iPhone, an 820px iPad and a 1470px MacBook side by side and scroll them together with Sync scroll, so a card that goes side by side in a slot that's too narrow is hard to miss.
A prompt for your AI tool
Refactor the card components on this site to use container queries instead of media queries. Put container-type: inline-size on each card's wrapper (in Tailwind v4: the @container class on the parent and @md:/@lg: variants on the card), never on the card itself. The default card layout is stacked for narrow slots; switch to side by side only when the container is at least 32rem wide. Size card headings with clamp() using rem plus cqi. Keep media queries for the page grid, navigation, hover and reduced-motion rules. Then list each place the card is used and how wide its slot is at 360px, 820px and 1470px viewport width.
The short version
- Media queries ask about the screen. Container queries ask about the slot.
container-type: inline-sizeon a wrapper,@container (width >= …)for the children,cqifor sizes.- Supported in every major browser since February 2023.
- A three-column grid on a 1470px laptop gives cards about 373px: phone-sized. Only a container query knows that.
- Media queries for the page frame and device features, container queries for components.
- In Tailwind v4:
@containerplus@sm:,@md:,@lg:, and remember@lg(512px container) isn'tlg(1024px screen).
Questions people ask
What is the difference between container queries and media queries?
A media query checks the viewport (the browser window) and device features such as hover. A container query checks the size of an element's container, so a component can change its layout based on the space it actually has.
Are CSS container queries supported in all browsers?
Yes. Size container queries and container query units have worked in Chrome, Edge, Safari 16+ and Firefox 110+ since February 2023, and MDN lists container-type as Baseline widely available.
Do container queries replace media queries?
No. Media queries are still the right tool for page layout and for device features like hover, orientation and reduced motion. Container queries are for components that appear in slots of different widths.
Does Tailwind support container queries?
Yes. Since Tailwind v4.0 they are built in: add the @container class to a parent and use variants like @md: or @lg: on its children. Tailwind v3 needs the @tailwindcss/container-queries plugin.
Why does my container collapse to zero width?
container-type: inline-size stops the element's width from depending on its content. That's fine for normal blocks and grid items, but an inline-block, float or flex item without a set width can shrink to nothing. Give it a width or put the container on a wrapper.