For more than 20 years, responsive design has meant one thing: media queries css that change the layout based on the viewport size. You write a breakpoint at 768 pixels, the page reflows, the designer signs off. Then container queries shipped, hit baseline support in 2024, and quietly rewrote the playbook. Now you can style a component based on the size of its parent container instead of the screen.
This guide walks through both. You will see when media queries are still the right answer, when container queries are dramatically better, and how to combine them in real layouts. Expect a lot of code blocks: a card built with media queries, the same card rebuilt with container queries, container query units like cqw for fluid typography, and a hybrid page layout that uses both at once.
If you build component libraries, design systems, or any UI that gets reused in different contexts, container queries will save you hours of duplicated CSS. If you ship a site that needs print stylesheets, dark mode, or reduced motion handling, media queries still own that job. By the end you will know which to reach for, and why.
Skip the setup. Try MobileViewer.io free.
20 free tests, no signup required. Test on 50+ real device frames in one click.
Try MobileViewer.io free →Why we needed two query types
For two decades, every responsive decision was tied to the viewport. That worked when pages were a single column of content with a sidebar. It stopped working the moment design systems started shipping reusable components.
Picture a product card. On the home page it sits in a three-column grid. On the product listing page it sits in a two-column grid. In the sidebar of a blog post it sits alone, in a 280-pixel column. With media queries, you only know the screen width. You do not know how wide the card's parent actually is. So the card breaks at 768 pixels regardless of context, and you end up writing custom overrides for every layout the card lives in.
Container queries fix this at the source. Declare the parent a container, ask that element about its width, and the card adapts to its own slot in the page. Drop it into a sidebar, a modal, a wide hero, anywhere. The card just works. This single feature unlocks true component-driven responsive design and removes the largest reason design systems get fragile over time.
Media queries are not going anywhere. They still own everything that is genuinely about the device or the environment. But for components, container queries are the better default in 2026.
CSS media queries css: a 2026 refresher
Media queries respond to the viewport. You write a condition, and when the screen matches, the styles inside apply. Most teams settle on five or six common breakpoints: 320, 480, 768, 1024, 1280, and 1536 pixels. There is no rule that says these are correct. They are convention.
Here is the canonical example, a mobile-first approach that adds desktop styles at 768 pixels and up:
/* Mobile-first base styles */
.hero {
padding: 1rem;
font-size: 1.25rem;
}
@media (min-width: 768px) {
.hero {
padding: 3rem;
font-size: 2rem;
}
}
You can also flip the model and write desktop-first by using max-width, although most modern teams write mobile-first because mobile traffic is the majority and smaller styles load first.
Modern CSS also supports range syntax, which reads far more naturally than chaining min and max:
/* Old way */
@media (min-width: 640px) and (max-width: 1024px) {
.card { background: #f5f5f5; }
}
/* New range syntax (Baseline 2023) */
@media (640px <= width <= 1024px) {
.card { background: #f5f5f5; }
}
Range syntax is supported in every evergreen browser. Use it. For more breakpoint guidance, see our deeper explainer on viewport breakpoints and our responsive design best practices.
Container queries: what they solve
Container queries flip the question. Instead of asking the viewport, you ask a containing element. You declare an element as a query container, then style its descendants based on that container's dimensions.
The minimum setup takes two steps. First, mark the parent as a container with container-type. Then write an @container rule that targets the container's width:
/* 1. Declare the container */
.container {
container-type: inline-size;
container-name: card;
}
/* 2. Style children based on the container's width */
@container card (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
}
container-type: inline-size tells the browser to track the container's inline dimension, which is width in a horizontal writing mode. container-name is optional but makes your queries readable, especially when components nest. Without a name, the nearest containing ancestor is used.
The mental model is simple: a component asks its parent how wide it is, then decides how to lay itself out. Drop the same card into a sidebar or a wide hero and it adjusts on its own. No new media queries, no overrides, no surprises.
Browser support for container queries in 2026
Container queries crossed the Baseline support line in 2023 and have been stable in every evergreen browser since. Safari shipped support in version 16, released September 2022. Chrome added support in version 105, August 2022. Firefox followed with version 110 in February 2023. Edge, Opera, and Brave inherit Chromium's support automatically.
The global usage share of container-query-capable browsers in May 2026 is above 96 percent for the United States and above 94 percent worldwide. Polyfills exist, but they are no longer necessary for production sites unless you have an unusual requirement to support legacy iOS Safari 15 or older Samsung Internet.
For container query units (cqw, cqh, cqi, cqb, cqmin, cqmax), support tracks the same release dates. If a browser supports @container, it supports the units.
One nuance to know: style queries (@container style(...)) shipped later and are still less universal in 2026. Size-based container queries, which is what most of this article covers, are fully ready. The MDN @container reference tracks the latest support and edge cases. Bookmark it.
Practical example: a card component (media query version)
Let's build the same card two ways. Start with the traditional media query approach. The card has an image and a body of text. Below 768 pixels it stacks vertically. At 768 pixels and above it goes horizontal.
.card {
display: block;
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
}
.card__image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
}
.card__body {
padding: 1rem;
}
@media (min-width: 768px) {
.card {
display: flex;
}
.card__image {
width: 40%;
aspect-ratio: 4 / 3;
}
.card__body {
flex: 1;
}
}
This works on the home page where the card sits in a 700-pixel column at desktop. The problem appears the moment you reuse the card. Drop the same card into a 300-pixel sidebar on a 1440-pixel desktop. The viewport is wide, so the media query fires, and you get a horizontal card crammed into a narrow column. The image is tiny, the text is squished, the design breaks.
The card has no idea it lives in a sidebar. The viewport says the screen is huge. The media query agrees. So the layout fights its actual context. You can fix it by writing per-layout overrides, but that scales badly. Every new layout is another override.
The same card with container queries
Now rebuild the card with container queries. The cards stay agnostic of where they live. The parent layout (sidebar, hero, modal, grid) is the container. The card just asks the container for its width.
/* Parent declares itself a container */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
.card {
display: block;
border: 1px solid #e5e7eb;
border-radius: 8px;
overflow: hidden;
}
.card__image {
width: 100%;
aspect-ratio: 16 / 9;
}
.card__body {
padding: 1rem;
}
/* Card asks the container, not the viewport */
@container card (min-width: 400px) {
.card {
display: flex;
}
.card__image {
width: 40%;
aspect-ratio: 4 / 3;
}
.card__body {
flex: 1;
}
}
Now drop the same card into a sidebar of 280 pixels. The container is narrow, so the card stays stacked. The image gets full width. The body text wraps naturally. Drop the card into a 900-pixel main column and it flips horizontal. Drop it into a 600-pixel modal and it flips horizontal at exactly the same trigger point (400 pixels container width). The card adapts to its actual surroundings, not the screen behind those surroundings.
This is the single biggest win container queries deliver: components that travel.
When to use media queries
Media queries still own everything that is about the device or the user's environment, not a specific component. Reach for media queries when:
- Page-level layout: the global header, the sidebar visibility toggle, the footer column count, the primary navigation drawer. These are tied to the screen, not to a component.
- Print stylesheets:
@media print { ... }remains the only way to style for paper. - Reduced motion:
@media (prefers-reduced-motion: reduce)respects user OS settings and should disable parallax, autoplay video, and decorative animations. - Color scheme:
@media (prefers-color-scheme: dark)remains the standard hook for dark mode. - Pointer and hover capability:
@media (hover: hover) and (pointer: fine)lets you separate hover styles from touch devices. - Display density and orientation: high-DPI displays and landscape/portrait shifts on tablets.
Anything tied to the device or environment is a media query job. The viewport is the right level of abstraction for those questions. If you switch a global header to a hamburger at 720 pixels, that is a page-wide decision based on the screen. A container query would be the wrong tool because the header is not a reusable component nested inside other containers.
When to use container queries
Container queries fit any component that lives in more than one context. Reach for them when:
- Reusable components: cards, tiles, list items, callouts, anything in a design system that ships with a name.
- Card grids and feeds: where a card might appear at 200, 400, 600, or 900 pixels of available width depending on the page.
- Sidebar widgets: subscription forms, related-post lists, mini player widgets that look different in a wide ad slot than in a narrow rail.
- Modal and drawer content: a contact form that sits inside both a wide modal and a narrow side drawer.
- Embedded widgets: anything an iframe or third-party script drops into an unpredictable host page.
The rule of thumb: if the component does not know its surroundings when you write the CSS, use a container query. If the component is always part of the global page chrome, use a media query. For an in-depth view of how this fits into your overall mobile strategy, our guide to viewing your website on mobile includes a checklist you can run during QA.
You can do the same kind of cross-device check in five seconds with MobileViewer.io, which renders your live URL inside 50+ real device frames simultaneously.
Combining both in a real layout
Most production sites end up using both. The page-level grid uses media queries. The components inside that grid use container queries. The two work together cleanly because they answer different questions.
Here is a real layout. The page has a hero, a three-column main grid on desktop, and a single column on mobile. Inside the grid, each cell holds a card that adapts to its column width.
/* Page layout: media queries decide column count */
.page-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
padding: 1rem;
}
@media (min-width: 768px) {
.page-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1200px) {
.page-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* Each grid cell is a container */
.page-grid > * {
container-type: inline-size;
container-name: card;
}
/* Card adapts based on the cell's width */
.card {
display: block;
padding: 1rem;
}
@container card (min-width: 380px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1rem;
}
}
@container card (min-width: 600px) {
.card {
grid-template-columns: 1fr 3fr;
padding: 2rem;
}
}
On a phone, the page-grid collapses to one column. Each card cell is 350 pixels wide, so the card stays stacked. On a tablet, two columns appear. Each cell is roughly 360 pixels, still stacked. On a desktop, three columns appear, each cell is 380 pixels and over, and the cards flip into a two-column inner layout. At very wide viewports the cells widen past 600 pixels and the card switches to its larger variant.
Two layers. Two responsibilities. Zero conflicts.
Container query units (cqw, cqh, cqi, cqb, cqmin, cqmax)
Container queries also unlock new length units that scale to the container, not the viewport. There are six:
cqw: 1 percent of the query container's width.cqh: 1 percent of the query container's height.cqi: 1 percent of the inline size (width in horizontal text).cqb: 1 percent of the block size (height in horizontal text).cqmin: the smaller ofcqiandcqb.cqmax: the larger ofcqiandcqb.
These let you build fluid typography that scales to its container instead of the viewport. Pair them with clamp() for natural minimum and maximum sizes:
.card {
container-type: inline-size;
}
.card__title {
/* Scales from 18px min to 32px max, fluid in between */
font-size: clamp(1.125rem, 5cqw, 2rem);
line-height: 1.2;
}
.card__body {
font-size: clamp(0.875rem, 3cqw, 1rem);
}
The title now grows or shrinks with the card itself, not with the screen. A card in a 280-pixel sidebar gets a smaller headline than the same card in a 900-pixel main column, without any breakpoints. Pitfall: when you nest containers, each cqw resolves against the nearest containing ancestor. Always declare container-name for nested setups and reference the right name in your queries.
Common gotchas with container queries
Container queries are well-engineered, but there are a few traps worth knowing.
Forgetting container-type. If you write @container (min-width: 400px) but never declare a container, nothing fires. Browsers fall back silently. Always verify the parent has container-type: inline-size or size.
Using container-type: size when you only need width. size contains both dimensions and forces a known height, which can break grid auto-sizing and intrinsic sizing. Use inline-size unless you specifically need height queries.
Naming collisions. Without container-name, the nearest ancestor with any container-type answers your query. When components nest, that can target the wrong element. Always name your containers in design systems.
Display: contents. Elements with display: contents are removed from the box tree and cannot be containers. If your wrapper uses it, container queries on that wrapper will not work.
Infinite layout loops. Theoretically possible if a child's size change affects its container's size, which then re-triggers the query. Browsers detect and bail out. Practically rare with inline-size.
Migrating older code from media to container queries
If you have an existing codebase full of media queries, you do not have to rewrite all of them. The migration that pays off is for reusable components specifically.
- Identify the candidates. Look for any component you reuse in more than one layout context: cards, tiles, list items, callouts. Page chrome (header, footer) is not a candidate.
- Declare the parent. Wrap the component (or pick its existing wrapper) and add
container-type: inline-sizeand a meaningfulcontainer-name. - Swap
@mediafor@container. Replace the@media (min-width: ...)rules that target the component with@container name (min-width: ...). Keep the same breakpoints to start. - Adjust the breakpoint values. Component breakpoints often differ from viewport breakpoints. A card that used to flip horizontal at 768 pixels viewport probably wants to flip at 400 pixels container.
- Test across contexts. Drop the component into a sidebar, a modal, a full-width hero. Confirm it adapts.
- Remove per-layout overrides. Old code probably had
.sidebar .card { ... }overrides. Delete them and let the container query do the work.
Migrate one component at a time. Ship between each. You will be surprised how much CSS disappears.
Testing your responsive design (don't skip this)
Container queries fire based on actual rendered container width, which means testing in DevTools alone is not enough. You need to verify how containers behave at real device widths and inside real layouts.
Three tools cover most cases. Chrome DevTools includes a responsive mode where you can resize the viewport freely, with the device toolbar showing container query overlays in the Elements panel. Firefox DevTools has the best CSS Grid and Flexbox inspectors in the industry, and the same overlay support for containers. For cross-device sweeps, our deep dive on how to see mobile view in Chrome covers DevTools workflows in depth.
For comparing how the same page looks on multiple devices at once, browser DevTools are a slow option. You resize, screenshot, resize again. MobileViewer.io renders your URL inside 50+ real device frames in one click, which is the fastest way to see if your container queries actually behave in the wild.
Whatever tool you use, the goal is the same: confirm the breakpoints fire where you expect, the layout reflows cleanly, and no component is fighting its container.
Test on 50+ real devices in one click.
Get 200 free tests when you sign up. Compare media query and container query layouts side-by-side across iPhones, iPads, Pixels, and more.
Start testing on MobileViewer.io →Performance: are container queries slower?
The short answer is no, not at any scale you will hit on a normal site. Browsers optimize @container rules with the same recalculation machinery they use for @media. Both queries are resolved during layout, both batch with style recalculation, and both have been profiled extensively by the Chromium and WebKit teams.
The one caveat is that container-type: size (which queries both width and height) forces extra layout containment and can interact badly with intrinsic sizing in nested grids. If you do not need height queries, stick with container-type: inline-size and there is no measurable performance cost.
The Chrome team's RAIL framework still applies: aim for layout work to stay inside the 16-millisecond frame budget. Container queries do not push you outside of that budget unless you nest containers ten levels deep with hundreds of children. For typical card grids, lists, and component libraries, the cost is identical to media queries.
One small gain: container queries often reduce total CSS size, because you replace per-layout overrides with one rule that adapts. Smaller stylesheets parse faster and render faster.
Media Queries vs Container Queries: side-by-side comparison
| Dimension | Media Queries | Container Queries |
|---|---|---|
| Responds to | Viewport width, height, and environment | A specific container's inline size or size |
| Best for | Page-level layout, print, dark mode, motion | Reusable components, cards, widgets, embeds |
| Setup required | None, just write @media | Parent needs container-type |
| Browser support (2026) | Universal, since 2012 | Baseline since 2023, ~96% users |
| Available units | vw, vh, vmin, vmax | cqw, cqh, cqi, cqb, cqmin, cqmax |
| Common mistake | Using viewport size for component decisions | Forgetting container-type on the parent |
Use this as a quick lookup. The simplest mental model: viewport for page, container for component. For the official syntax references, MDN keeps both pages up to date: the @media documentation and the web.dev container queries guide are both worth bookmarking.
Conclusion: pick the right tool for the right job
Media queries did one job for 20 years and did it well. They still do that job. For the global header, the print stylesheet, the dark mode toggle, the reduced motion handler, media queries are the right answer.
Container queries do a different job, and they do it well too. For cards, tiles, sidebar widgets, modal contents, and any component that ships inside a design system, container queries unlock layouts that travel cleanly between contexts. The component asks its parent how much room it has, and adapts.
Use both. Layout the page with media queries. Style components with container queries. Test on real devices to make sure your breakpoints fire where you expect. Your future self, the one debugging a sidebar card that broke on a tablet, will thank you.
Frequently asked questions
What's the difference between media queries and container queries?
Media queries respond to the viewport (the browser window) while container queries respond to a specific parent element you mark as a container. A media query asks, how wide is the screen? A container query asks, how wide is this card's parent? That difference matters because the same component can sit in a 300-pixel sidebar or a 900-pixel main column. Media queries cannot tell the difference. Container queries can, which lets components style themselves based on their actual context.
Is container queries supported in all browsers in 2026?
Container queries hit Baseline browser support in 2023 and are now available in every evergreen browser. Safari supports them from version 16 (September 2022), Chrome from 105 (August 2022), Firefox from 110 (February 2023). As of May 2026, container-query-capable browsers cover over 96 percent of users in the United States and over 94 percent worldwide. Polyfills are no longer needed for normal production sites. Test on real devices to verify, especially older iOS Safari versions.
When should I use a container query instead of a media query?
Use a container query whenever a component might appear in more than one layout context: cards, tiles, sidebar widgets, modal contents, list items, embed components. These components do not know how wide their parent will be when you write the CSS, so they need to ask. Use a media query for page-level decisions: the global header, the footer column count, the navigation drawer, print styles, dark mode, reduced motion. Anything tied to the device or environment, not a specific component.
Can I use both media queries and container queries in one stylesheet?
Yes, and you should. Most production sites do. Media queries handle the page-level layout, while container queries handle the components inside that layout. They do not conflict because they answer different questions. A media query might change the page from one column to three, and a container query might change how each card inside those columns reflows. The two work together cleanly and produce less total CSS than either approach alone.
Are container queries slower than media queries?
No, not at any scale you will hit on a normal site. Browsers resolve @container rules using the same machinery as @media, and both are batched with normal layout and style recalculation. The Chromium and WebKit teams have profiled both extensively. The only edge case is container-type: size, which queries height as well as width and can interact with intrinsic sizing. Stick with container-type: inline-size and you will not see any measurable performance difference.
What does container-type: inline-size do?
container-type: inline-size tells the browser to track the container's inline dimension, which is the width in a horizontal writing mode (the default for English and most Western languages). Once set, descendants can use @container queries based on that width and use container query units (cqw, cqi). Use it on any element you want to query the width of. Use container-type: size only when you also need to query height, since it forces extra layout containment.
Do container queries work with display: grid?
Yes. CSS Grid items can be containers, grid containers can be containers, and @container queries fire correctly inside grid layouts. The only thing to watch out for is display: contents, which removes an element from the box tree and prevents it from being a container. Grid layouts pair extremely well with container queries: a grid auto-sizes the column widths, each cell is a container, and each card adapts to its actual cell width. This is the most common production pattern.
Do I still need media queries if I use container queries?
Yes. Container queries do not replace media queries; they complement them. Media queries remain the only way to handle environment-level concerns: print stylesheets (@media print), reduced motion, dark mode, pointer and hover capability, orientation, and high-DPI displays. They are also still the best tool for global page layout where you do not have a meaningful container element. The right mental model is: media queries for the page, container queries for the components.