Skip to content
C Codeloom
HTML & CSS

CSS Container Queries Explained with Real Examples

Container queries let components style themselves based on their parent's size, not the viewport. Learn the syntax, the units, and when to use them.

·4 min read · By Codeloom
Intermediate 9 min read

What you'll learn

  • Why media queries are not enough for components
  • How container-type and container-name work
  • How to write @container rules
  • How container query units like cqw differ from vw
  • How to refactor a card to be container-responsive

Prerequisites

  • Basic CSS, including media queries

What and Why

For years, responsive design meant “look at the viewport and adjust.” That works for the page as a whole. It fails for a reusable component dropped into a sidebar one day and a full-width hero the next: the viewport is identical, but the available space is not.

Container queries solve this. A component can ask, “How wide am I right now?” and style itself accordingly, regardless of the viewport. The result: truly portable components.

Mental Model

A container query has two parts: a container declared on some ancestor, and @container rules that react to that container’s size.

Media query:    asks the viewport (window)
 @media (min-width: 600px) { ... }

Container query: asks the nearest matching container
 @container (min-width: 400px) { ... }
Media query vs container query

You opt an element in as a container by setting container-type. The two main values are inline-size (most common; queries can ask about width) and size (both axes; requires the element to have a definite size).

Hands-on Example

A product card that switches from a stacked layout to side-by-side once it has room:

<article class="card">
  <img src="/p.jpg" alt="" />
  <div class="body">
    <h3>Wireless Headphones</h3>
    <p>Studio-grade sound, 30 hour battery.</p>
    <button>Add to cart</button>
  </div>
</article>
.card {
  container-type: inline-size;
  container-name: card;
  display: grid;
  gap: 1rem;
  padding: 1rem;
  background: #fff;
  border-radius: 0.75rem;
}

.card img { width: 100%; height: auto; }

@container card (min-width: 420px) {
  .card {
    grid-template-columns: 160px 1fr;
    align-items: start;
  }
  .card img { width: 160px; }
}

Now drop this card in a 320px sidebar and it stacks; drop it in a 900px main column and it goes side-by-side. The card does not know or care about the viewport.

You can also use the shorthand:

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

Container query units scale to the container, not the viewport. 1cqw is 1% of the container’s width; 1cqh is 1% of its height. Useful for fluid typography inside a component:

.card h3 {
  font-size: clamp(1rem, 0.9rem + 1.2cqw, 1.5rem);
}

The heading scales with the card’s width, capped by min and max. A small card gets a smaller title; a wide one gets a bigger one, without media queries.

You can query container styles too (not just size) for advanced theming, though browser support varies. For most projects, @container (min-width: ...) is the workhorse.

Common Pitfalls

Forgetting to set container-type. Without it, @container rules never match. The default is normal, which is opt-out.

Putting container-type: size on an element without explicit dimensions. The query has nothing to measure and the rules do not fire. inline-size is safer because it only constrains the inline axis.

Querying the wrong container. @container matches the nearest ancestor that is a container; if multiple ancestors qualify, name them and use @container card (...) to disambiguate.

Layout shifts due to containment. container-type implies contain: layout (and more for size), which can affect how absolutely positioned children behave. Test that overlays and tooltips still appear in the right place.

Treating container queries as a replacement for media queries. They complement each other: media queries handle page-level decisions (sidebar visibility, font size baseline), container queries handle component-level adaptations.

Practical Tips

Wrap reusable components in a container so they can be dropped anywhere. The wrapping element becomes the public boundary of the component.

Name your containers when a component is nested inside another component that is also a container. Without names you may target the wrong one.

Use cqi (inline size of the container) for fluid type and spacing inside the component. It behaves like a “self-aware vw”.

Test components in at least three container widths: narrow (300px), medium (500px), wide (800px). A storybook-style harness pays for itself the first time you spot a wrap that you missed with the dev tools at 1440px.

Combine container queries with logical properties (padding-inline, margin-block) for layouts that handle both responsive sizes and right-to-left languages cleanly.

Wrap-up

Container queries shift responsive design from the page to the component. Declare a container, write @container rules against its size, and use cq* units for fluid internals. The payoff is components that look right wherever they land, and CSS that stops leaking layout assumptions across files.