AniUI Academy

Container Queries

Style a component based on its container's size, not the viewport — so one card lays out one way in a sidebar and another in a wide main column, with no viewport math or JavaScript.

10 min read

Every responsive-design lesson so far has been built on media queries: the viewport is some size, so the layout adapts. That model works until you try to build a reusable component. A card that looks great at 400px of viewport width looks cramped at 400px of sidebar width, because the card doesn't know it's in a sidebar — it only knows what the viewport is.

Container queries fix that gap. A component queries its own container's size, not the viewport's, and styles itself based on the space it was actually given.

The breakage media queries can't fix

Picture a .card component with an image and a caption. At narrow widths you want it stacked (image on top, caption below); at wider widths you want them side by side.

.card {
  display: grid;
  grid-template-columns: 1fr; /* stacked */
}
 
@media (min-width: 500px) {
  .card {
    grid-template-columns: 200px 1fr; /* side-by-side */
  }
}

That works for exactly one layout: the one where the card takes roughly the full viewport width. Put the same card in a 300px sidebar at a 1200px viewport, and the media query fires at the viewport width (1200px), the card goes side-by-side, and the whole thing is unreadably cramped because the "side" is 100px wide.

There's no viewport math that gets this right in every layout the component can land in, because the viewport is the wrong thing to measure.

The query container

A container query has two sides: opt a parent element in as a query container, then query it from inside.

.card-wrapper {
  container-type: inline-size;
}
 
.card {
  display: grid;
  grid-template-columns: 1fr; /* stacked */
}
 
@container (min-width: 500px) {
  .card {
    grid-template-columns: 200px 1fr; /* side-by-side */
  }
}

Now the card reacts to the width of .card-wrapper, not the viewport. Drop the wrapper into a sidebar, the card stays stacked. Drop it into a 1200px main column, the card goes side-by-side. Same component, different context, correct result.

container-type: inline-size vs size

inline-size opts in to querying the container's horizontal size only (in a typical left-to-right document). This is what you'll use almost every time. size queries both dimensions, but it requires the container to have a definite height, which most components don't, so it's a usability footgun.

Naming containers

If a card sits inside multiple nested containers, you'd match against the nearest one by default. To target a specific ancestor, name it:

.layout {
  container-type: inline-size;
  container-name: layout;
}
 
.card-wrapper {
  container-type: inline-size;
  container-name: card-area;
}
 
@container card-area (min-width: 500px) {
  .card {
    grid-template-columns: 200px 1fr;
  }
}

The container shorthand sets both at once: container: card-area / inline-size.

Container query units

The second half of the feature is the new container-relative units:

  • cqi — 1% of the container's inline size.
  • cqb — 1% of the container's block size.
  • cqw, cqh — 1% of the width and height respectively.
  • cqmin, cqmax — the smaller / larger of cqi and cqb.

These are to a query container what vw/vh are to the viewport. The common use is fluid typography that scales with the component, not the window:

.card-title {
  font-size: clamp(1rem, 4cqi, 1.75rem);
}

The title is at least 1rem, at most 1.75rem, and in between it scales linearly with the container's inline size. A reusable component now sizes its text to its own space without ever writing a container query.

Browser support

Container queries have been baseline available since 2023 — all major browsers support them. In practice, if you're writing CSS in 2026, you can reach for them the same way you reach for Flexbox or Grid.

The one historical caveat worth knowing is that container-type establishes containment on the element, which can affect layout in a couple of edge cases (an element can no longer size itself to its children's intrinsic size in the queried axis). In day-to-day use, this is fine — the containers you opt in are almost always layout-holding wrappers whose size is set by their own parent.

Where container queries change how you write components

Three patterns that were painful with media queries become straightforward:

  • Reusable cards. The card's layout is driven by the card's space, not the page's. One component, every context.
  • Design system components in multiple themes of app. A library component dropped into a marketing site with a wide main column and into an internal tool with a split sidebar layout works in both without a props-driven layout override.
  • Nested layouts. A dashboard pane that contains cards that contain mini-charts — each level is its own container, and each level's children adapt to the level above without running measurements in JavaScript.

The habit worth forming is: when you're writing a component that could be dropped into more than one layout, think about what the component's container might be rather than what the viewport is, and make the opt-in container at the layout level.

What to remember

  • Media queries react to the viewport. Container queries react to a specific ancestor element — the right tool for a reusable component.
  • container-type: inline-size is what you want 95% of the time. size requires a definite height and is a common gotcha.
  • Name containers with container-name when you have more than one in the ancestor chain. The shorthand is container: name / type.
  • Container query units (cqi, cqb, cqw, cqh) scale relative to the query container — useful with clamp() for fluid, container-relative typography.
  • Container queries are baseline-available; reach for them the same way you reach for Flexbox or Grid.
  • Writing habit: ask "what container is this in?" instead of "what viewport is this in?" when designing a reusable component.

Check yourself

4 questions · pass 3/4 to unlock Responsive Images

up to 50
  1. 1.What is the one-sentence difference between a media query and a container query?

  2. 2.What does container-type: inline-size on a parent element do?

  3. 3.What's the point of the cqi, cqw, cqb and cqh units?

  4. 4.A reusable .card component needs to switch from a stacked (image-on-top) to a side-by-side (image-on-left) layout when it has at least 500px of width — but only based on its own column, not the viewport. Which shape is correct?

4 left to answer

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.