TH The Sass Way
Responsive Design

CSS Container Queries: Style Components by Their Own Size

CSS Container Queries: Style Components by Their Own Size
tldrCSS container queries style an element based on the size of its parent container rather than the viewport. You declare a container with container-type: inline-size on a wrapper element, then write @container rules — for example @container (min-width: 400px) — that apply styles to the component inside whenever the container crosses that width. This lets one reusable component adapt automatically to a sidebar, a grid column, or a full-width slot, and it works in all modern browsers.

What Are CSS Container Queries?

CSS container queries let you style an element based on the size of its parent container instead of the size of the browser viewport. You mark an ancestor element as a container with the container-type property, then write @container rules that apply styles whenever that container is wider (or narrower) than a threshold you choose.

That one shift solves a problem media queries never could: a component's layout can now respond to the space it's actually given. Drop the same card component into a narrow sidebar and a wide main column on the same page, and it can render as a compact stacked card in one spot and a roomy horizontal card in the other — no extra classes, no JavaScript, no duplicate CSS.

Container queries are supported in all modern browsers and have been safe for production use since early 2023. If you build reusable components — and in 2026, almost everyone does — they belong in your toolkit next to media queries, not as a someday feature.

Container Queries vs Media Queries

Media queries ask, "How big is the viewport?" Container queries ask, "How big is the box this component lives in?" Both are essential; they just operate at different levels of your design.

Media queries Container queries
Responds to Viewport (or device features) A designated ancestor element
Best for Page-level layout: grids, navigation, column counts Component-level layout: cards, widgets, form rows
Setup required None — works anywhere An ancestor needs container-type
Same component, two contexts Needs modifier classes or duplicated rules Adapts automatically to each context
Query units vw, vh, and friends cqw, cqh, cqi, cqb, cqmin, cqmax
Syntax @media (min-width: 768px) @container (min-width: 400px)

A practical rule of thumb: use media queries to decide how many columns the page has, and container queries to decide how each component behaves inside its column. The page-level thinking is covered in depth in our responsive design guide; this article handles the component level.

Step 1: Create a Container with container-type

Nothing responds to a container query until you declare a container. You do that on the element whose size you want to measure — typically a wrapper around your component, not the component itself.

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

container-type accepts three values:

Why inline-size is the safe default

Declaring a query container applies containment to the element. With container-type: inline-size, the element's width can no longer be sized by its contents — it must come from its context, like a grid track, a flex basis, or a plain block-level width. In practice that's how most wrappers already work, which is why inline-size rarely causes surprises. Full size containment also locks down the height, which is why it's the riskier choice.

Naming containers

When containers nest — a card inside a sidebar inside a page — a @container rule resolves against the nearest ancestor container by default. To target a specific one, give it a name:

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

Or use the shorthand, which takes the name first, then the type:

.sidebar {
  container: sidebar / inline-size;
}

Step 2: Write @container Rules

With a container declared, @container works much like @media: a condition, then a block of normal rules.

.card {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@container (min-width: 400px) {
  .card {
    grid-template-columns: 120px 1fr;
    align-items: center;
  }
}

Here the card stacks by default and switches to an image-beside-text layout whenever its container is at least 400px wide — regardless of what the viewport is doing. Note that this is the same mobile-first pattern you already use with media queries: start with the narrow layout, then enhance upward. If that workflow is new to you, our mobile-first CSS guide explains why it produces simpler stylesheets.

To query a named container, put the name before the condition:

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

Modern range syntax works too, and reads more naturally:

@container (width > 400px) {
  .card {
    grid-template-columns: 120px 1fr;
  }
}

One rule to remember: a container query can't style the container it queries. The styles inside @container apply to descendants of the container. If you need to restyle the wrapper itself, query an ancestor container above it.

Container Query Units: cqw, cqh, and Friends

Container queries ship with their own length units, mirroring viewport units but measured against the queried container:

Their killer use case is fluid typography that scales with the component, not the page:

.card h2 {
  font-size: clamp(1.125rem, 4cqi + 0.5rem, 1.75rem);
}

Now the same heading is modest in a cramped sidebar and generous in a wide feature slot, with no breakpoints at all. Prefer cqi over cqw when you can — it respects vertical writing modes for free.

You can use these units anywhere a length is valid, even outside @container blocks. If no ancestor container exists, they fall back to behaving like the corresponding small viewport units, so they degrade sensibly. For a refresher on how all the CSS length units relate, see our guide to CSS units.

Using Container Queries with Sass

Sass passes @container through to the compiled CSS untouched, and nesting works exactly as it does with @media. That means you can keep component variants next to the component:

@mixin container-up($min) {
  @container (min-width: #{$min}) {
    @content;
  }
}

.card {
  display: grid;
  gap: 1rem;

  @include container-up(400px) {
    grid-template-columns: 120px 1fr;
  }
}

Dart Sass compiles that to:

.card {
  display: grid;
  gap: 1rem;
}
@container (min-width: 400px) {
  .card {
    grid-template-columns: 120px 1fr;
  }
}

Two Sass-specific notes:

A Real-World Example: One Card, Three Contexts

Here's the pattern that sells container queries to every team that tries them. Imagine a product card used in three places: a 300px sidebar, a three-column grid, and a full-width feature slot.

.product-slot {
  container-type: inline-size;
}

.product-card {
  display: grid;
  gap: 0.75rem;
}

/* Enough room: image moves beside the text */
@container (min-width: 380px) {
  .product-card {
    grid-template-columns: 140px 1fr;
  }
}

/* Lots of room: bigger image, roomier spacing */
@container (min-width: 640px) {
  .product-card {
    grid-template-columns: 220px 1fr;
    gap: 1.5rem;
  }
}

Every place the card appears, its slot is the container. In the sidebar, neither query matches and the card stacks. In the grid, each column is around 400px, so the first query kicks in. In the feature slot, both match and the last one wins, exactly like the mobile-first cascade you already know. The card's CSS never mentions the sidebar, the grid, or the feature slot — which means next month's brand-new context works without touching this file.

Compare that with the media-query version: you'd need to know every viewport width at which each slot changes size, encode those page-level details into the component's styles, and update them whenever the page layout changes. That coupling is exactly what container queries remove.

Container Queries Browser Support

Size container queries and container query units are supported in every current major browser: Chrome and Edge (from version 105), Safari (from 16), and Firefox (from 110). All of those releases shipped by early 2023, so unless your audience includes very old browsers, you can use container queries today without a build step or polyfill.

Two related features are worth tracking separately:

Common Mistakes to Avoid

The Bottom Line

Container queries close the gap that made truly reusable components impossible in CSS: components can finally own their responsive behavior. Declare a wrapper with container-type: inline-size, write @container rules the same mobile-first way you write media queries, and reach for cqi units when you want fluid sizing inside the component. Keep media queries for the page grid, hand everything inside it to container queries, and your components will work in every slot you'll ever drop them into — including the ones you haven't designed yet.

FAQ

What is the difference between container queries and media queries?

Media queries respond to the browser viewport; container queries respond to the size of a designated ancestor element. Media queries suit page-level decisions like how many columns a layout has, while container queries suit component-level decisions like whether a card shows its image beside or above its text. They complement each other: use @media for the page grid and @container for the components placed inside it.

How do I enable container queries in CSS?

Add container-type: inline-size to the ancestor element you want to measure — typically a wrapper around your component, not the component itself. Then write an @container rule such as @container (min-width: 400px) { .card { display: flex; } }. The styles inside apply to descendants of the container whenever its width meets the condition. Without a declared container-type on an ancestor, @container rules never match.

What does container-type: inline-size do?

It makes the element a query container measured by its inline dimension — its width in horizontal writing modes — and applies inline-size containment, meaning the element's width must come from its context rather than its contents. It's the recommended value for almost all cases. The alternative, container-type: size, also allows height-based queries but locks down the element's height, which breaks layouts where height should grow with content.

What are container query units like cqw and cqi?

They are length units measured against the queried container instead of the viewport: cqw and cqh are 1% of the container's width and height, cqi and cqb are 1% of its inline and block size, and cqmin and cqmax take the smaller or larger dimension. They're ideal for fluid, component-scoped typography, such as font-size: clamp(1.125rem, 4cqi + 0.5rem, 1.75rem). Prefer cqi, which respects writing modes.

Are CSS container queries supported in all browsers?

Yes — size container queries and container query units work in every current major browser: Chrome and Edge from version 105, Safari from 16, and Firefox from 110, all released by early 2023. No polyfill or build step is needed for modern audiences. Style queries, which respond to custom property values instead of size, arrived later and rolled out gradually, so check current support tables before using them.

Can a container query style the container itself?

No. The styles inside an @container rule apply only to descendants of the container being queried, because letting a container restyle itself based on its own size could create infinite loops. If you need to change the wrapper's own styling, declare a container higher up the tree and query that ancestor instead. This is also why you should declare the container on a wrapper rather than on the component you're styling.