Elementary Standards
HTMLCSSWeb StandardsSEOBlogStart reading
Home / Blog / CSS Container Queries: A Practical Guide for 2026
CSS

CSS Container Queries: A Practical Guide for 2026

Media queries respond to the viewport; container queries respond to the component's own box — here's the syntax, the gotchas, and when to actually reach for it.

A card component looks fine in the three-column grid where you designed it, then breaks the moment someone drops it into a 240px sidebar slot. The text overflows, the image squashes, the layout that worked at full width falls apart — because the component was styled with media queries, and media queries only know one thing: how wide the browser window is. They have no idea the component is now sitting in a sidebar.

TL;DR: container queries let an element respond to the size of its nearest sized ancestor instead of the viewport, via container-type on the ancestor and @container rules on the descendants. New cqw/cqh/cqi/cqb units size type and spacing relative to that same container. Use container queries for reusable components; keep media queries for page-level layout.

This is exactly the problem component libraries and design systems run into constantly, and it's a close cousin of what SaaS product teams deal with every time they build a dashboard that has to reflow the same widget across a grid view, a list view, and a narrow sidebar — the kind of context-independent UI architecture that SaaS application development at YuSMP has to get right by default, because the alternative is a different stylesheet per layout slot.

Container Queries vs. Media Queries — What's Actually Different?

A media query asks the browser one question: how big is the viewport? It doesn't matter where in the page a given element lives — every media query on the page gets the same answer, because they're all asking about the same window.

A container query asks a different question entirely: how big is this specific ancestor box? You pick which ancestor by marking it as a container, and from that point on, any descendant can write rules that respond to that box's size, regardless of how wide the overall page is.

This distinction matters most for component libraries and design systems, where the same card, widget, or panel gets reused across layouts its author never anticipated. A product card in a three-column grid, a one-column mobile stack, and a 280px cart sidebar all have wildly different available widths at the exact same viewport size. Viewport width tells you nothing useful there — container width tells you everything.

Setting Up a Container: container-type and container-name

Before any @container rule can match anything, some ancestor element has to opt in to being queried. That's what container-type does:

.card-wrapper {
  container-type: inline-size;
}

Three values are available, and picking the right one matters:

A single page often has more than one container nested inside another — a card inside a sidebar that is itself a container, for instance. Without a name, an @container rule matches whichever containing ancestor is nearest, which is not always the one you meant. container-name removes the ambiguity:

.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

@container sidebar (min-width: 300px) {
  .widget { flex-direction: row; }
}

Naming containers isn't optional polish once you have more than one level of nesting — it's the difference between a query that reliably targets the box you intended and one that silently binds to whichever ancestor happens to be closest.

Writing the Query: the @container At-Rule

Once an ancestor is a named (or unnamed) container, descendants write rules against it with @container, using the same comparison syntax media queries use:

.card-wrapper {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

There's also a shorthand that sets both the type and the name on one declaration: container: sidebar / inline-size; is equivalent to writing container-name: sidebar; container-type: inline-size; as two separate properties. Most real stylesheets use the longhand for readability, but the shorthand is worth recognizing when you see it in someone else's code.

The New Units: cqw, cqh, cqi, cqb

Container queries shipped with their own set of relative length units, parallel to the familiar viewport units (vw, vh) but scoped to the nearest query container instead of the viewport:

These units work independently of whether you've written any @container rule at all — they're a sizing tool, not a query tool. A heading inside a card can scale smoothly with the container's own width, with no breakpoint logic involved:

.card-wrapper {
  container-type: inline-size;
}

.card h3 {
  font-size: clamp(1rem, 8cqi, 1.5rem);
}

That one line ties the heading's size to the card's own box, not the screen. Drop the same card into a wide feature slot or a narrow sidebar and the type scales proportionally to whichever space it actually has, with no separate rule for each context.

Container Style Queries — the Quieter, More Powerful Half

Size isn't the only thing a container can report. A container style query lets descendants write rules based on the computed value of a custom property on the container, using the style() function inside @container:

@container style(--theme: dark) {
  .card {
    background: #0d111b;
    color: #fff;
  }
}

This flips the usual direction of a CSS variable: instead of a component reading a variable to set its own value, the component's styling branches on whatever value a variable currently holds on its container — a theme toggle, a density setting, a state flag — without any JavaScript class-swapping to make it work. It's a genuinely useful primitive for design systems that need a component to respond to context other than raw size.

Style queries are younger and less universally supported than size queries, and that gap is worth being honest about rather than papering over with a version-number table that will be stale within a quarter. Check current support on MDN's container queries reference before relying on style queries for anything load-bearing.

When Should You Actually Reach for a Container Query?

The rule of thumb that holds up in practice: media queries for the page shell — the overall grid, the navigation pattern, whether a sidebar exists at all. Container queries for anything meant to be dropped into more than one layout context — cards, widgets, sidebar modules, dashboard panels.

If you can imagine the same component living in a wide feature slot on one page and a narrow list-item slot on another, it's a container-query candidate. If a style decision is really about the page as a whole — "collapse the nav into a hamburger below 768px" — that's still squarely a media query's job. This is precisely the kind of reusable, context-independent component work that shows up constantly in SaaS product UIs, where the same dashboard widget has to reflow correctly whether it's pinned to a grid, stacked in a list, or squeezed into a sidebar.

A Worked Example: A Card That Reflows on Its Own

Here's the complete pattern end to end — a card whose internal layout and image size change based on its own container, independent of the viewport:

.card-wrapper {
  container-type: inline-size;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.card img {
  width: 100%;
}

@container (min-width: 380px) {
  .card {
    flex-direction: row;
    align-items: center;
  }
  .card img {
    width: 40%;
  }
}

Below 380px of available container width, the card stacks: image on top, text below, full-width image. Above that threshold, it switches to a row layout with a smaller image alongside the text — and this happens identically whether the card sits in a 1400px-wide feature column or a 900px-wide page squeezed into a 380px grid slot. The viewport is irrelevant to the decision; only the card's own box matters.

Browser Support — Where Things Stand

Container size queries and the cq* units are supported across all current evergreen browsers — Chrome, Edge, Firefox, and Safari all ship them. Container style queries are newer and landed later, so support is less universal; treat them as a progressive enhancement rather than a baseline feature for now. Rather than reproducing a version table here that will drift out of date, check MDN's container queries page or caniuse.com for the current state before shipping style queries as load-bearing.

FAQ

What's the difference between a container query and a media query?

A media query reads the viewport — the browser window's width or height. A container query reads the size (or, with style queries, a custom-property value) of a specific ancestor element you've designated as a container with container-type. The same component can report two different sizes to the two systems at once, because one is asking about the screen and the other is asking about a box.

Do I need container-type on every element I want to query?

Not on the element itself — you need container-type on an ancestor of the element whose rules change, typically the immediate wrapper. The @container rule then targets descendants of that wrapper. Forgetting to set container-type anywhere in the chain is the single most common reason an @container rule silently does nothing.

What happens if I forget container-name with nested containers?

An unnamed @container query matches the nearest ancestor with a matching container-type, which is whichever one happens to be closest — not necessarily the one you meant. Nest two containers without names and a query written for the outer one can silently bind to the inner one instead, a query collision that produces no error, just the wrong layout.

Can I use container queries and media queries together?

Yes, and in most real layouts you should. Media queries are still the right tool for page-level decisions — the overall grid, the navigation pattern, whether a sidebar exists at all. Container queries then handle how an individual component reflows once it has been dropped into whatever slot that page-level layout created for it.

Are container query units (cqw, cqh) supported everywhere size queries are?

In every browser that ships size container queries, yes — the units and the at-rule landed together as one feature. They work independent of whether you've written an @container rule at all; you can use cqw or cqi inside any element that has a sized container ancestor, purely for fluid sizing, with no query block involved.

Do container queries hurt performance?

Setting container-type: size forces layout containment on both axes, which has a real (if usually small) layout cost, because the browser can no longer let that element's size depend on its children. inline-size avoids most of this by only containing the inline axis. For the common case — a component that reflows based on width — inline-size is both the right semantic choice and the cheaper one.

Can I polyfill container queries for older browsers?

Partially. A handful of JavaScript polyfills (notably GitHub's container-query-polyfill) replicate size-based @container behavior using ResizeObserver, but they don't cover style queries, and shipping a runtime polyfill for a purely visual breakpoint is often not worth the weight. The more common approach is progressive enhancement: ship a reasonable single-column fallback and layer the container-query rules on top for browsers that support them.

Container queries don't replace media queries — they fill the gap media queries were never designed for. Reach for container-type and @container when a component needs to work in more than one place, keep media queries for the page shell, and the two systems stay out of each other's way.

Back to the blog
More notes

Keep reading