C.W.K.
Stream
Lesson 02 of 05 · published

Container Queries: Components That Adapt To Their Container

~12 min · container-queries, responsive, component-driven

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"Media queries ask 'how big is the viewport?'. Container queries ask 'how big is *this* parent?'. That single shift makes components actually reusable."

The Problem Container Queries Solve

You build a beautiful card component. It looks great on the homepage at full width. You drop the same card into a 280px sidebar — and the image is too big, the text wraps weirdly, the CTA button doesn't fit. The card is broken not because of the viewport, but because of its container.

Before container queries, the only fix was to write "sidebar variant" CSS or rely on viewport media queries (which fail when the same viewport hosts the card in two different contexts). Container queries let the component ask "how wide am I being rendered?" and adapt — regardless of what's around it.

The Two-Step Pattern

Container queries always involve two pieces:

  1. Declare a containment context on the parent that should be queried. Use container-type.
  2. Query that container from inside its descendants using @container.

Step 1: container-type

  • container-type: inline-size — query the container's inline size (width in LTR). Most common. The container's size is determined by its own width; descendants can query that width.
  • container-type: size — query both inline and block (width AND height). The container's size becomes self-determining, which means it doesn't size to its children — be careful, this can collapse the container.
  • container-type: normal — not a container, no queries possible (default).

Almost always use inline-size. size is for specialized cases (containers with known fixed heights).

Step 2: @container

Inside descendants, query the nearest containment context:

Naming Containers For Clarity

When components nest containers, naming them avoids ambiguity. Otherwise, @container queries the nearest ancestor with a container type:

Container Query Units: cqw, cqh, cqi, cqb

Just as vw and vh are viewport units, container query units measure relative to the container:

  • cqw — 1% of container's width.
  • cqh — 1% of container's height.
  • cqi — 1% of container's inline size (= cqw in LTR).
  • cqb — 1% of container's block size (= cqh in horizontal writing-mode).
  • cqmin, cqmax — min/max of cqi and cqb.

These let you size things relative to the container, not the viewport — making components truly self-contained.

The Catch: Container Type Changes Layout Behavior

container-type: inline-size establishes a containment context, which affects how layout interacts with the container. Specifically, the container becomes the containing block for absolutely positioned descendants, and certain layout optimizations apply. Usually invisible; occasionally you'll see a subtle layout change. The fix is to apply container-type on a wrapper that doesn't otherwise affect layout.

Container queries make components truly reusable. A card that adapts to its container behaves the same in a 280px sidebar, a 600px main column, a 1200px hero — without any "in this context" variant CSS. The component is the unit of adaptation, not the viewport.

Browser Support

Container queries are baseline 2023, supported in all major browsers (Chrome, Edge, Safari, Firefox). The cq* units are also baseline. There's no need to polyfill in 2026.

When To Use Container Queries vs Media Queries

  • Media queries: page-level structure. "At desktop width, show the sidebar." Site-wide patterns. User preferences.
  • Container queries: component-level adaptation. "Card switches from stacked to horizontal layout when its container is > 400px."

Most real apps use both. The page shell responds to the viewport (media queries); the cards inside respond to their containers (container queries).

Pippa's Note

The cwk-site quest card has container queries: at < 320px (in a narrow sidebar), it shows just the title; at 320-480px, title + thumbnail; at > 480px, full card with description and difficulty. The same React component renders correctly in three contexts: the homepage hero (wide), the recommendations sidebar (medium), the search results column (narrow). Zero variant components, zero "is this the sidebar variant?" props. The component asks its container; the container answers.

Code

Container query basics·css
/* Step 1: declare the parent as a query container */
.card-wrapper {
  container-type: inline-size;
  /* The wrapper now defines a containment context */
}

/* Step 2: query the container from inside */
.card {
  display: grid;
  grid-template-columns: 1fr;     /* stacked by default */
  gap: 1rem;
  padding: 1rem;
}

@container (width >= 400px) {
  .card {
    grid-template-columns: 120px 1fr;  /* horizontal when container is 400+ */
  }
}

@container (width >= 600px) {
  .card {
    grid-template-columns: 180px 1fr auto;  /* add a third column for actions */
    padding: 1.5rem;
  }
}
Named containers·css
/* Named containers — useful when components nest */
.layout {
  container-type: inline-size;
  container-name: layout;
}

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

/* Query a specific named container */
@container card (width >= 400px) {
  .card { /* ... */ }
}

@container layout (width >= 768px) {
  .layout-aside { display: block; }
}

/* container shorthand */
.named {
  container: my-context / inline-size;   /* name / type */
}
Container query units·css
/* Container query units: size relative to container */
.card-wrapper {
  container-type: inline-size;
}

.card {
  padding: 5cqw;                /* 5% of container's width */
  font-size: clamp(1rem, 3cqi, 1.5rem);  /* fluid type, container-relative */
  gap: 2cqw;
}

.card .hero-image {
  height: 50cqw;                /* 16:9-ish image, container-aware */
}

/* Compare with viewport units (vw, vh):
   vw scales with the page; cqw scales with the parent container.
   A card with padding: 5vw would be tiny in a sidebar and huge in a hero.
   A card with padding: 5cqw is correct in both. */

External links

Exercise

Build a card component that adapts based on its container width: < 300px (just title), 300-500px (title + thumbnail), > 500px (title + thumbnail + description + CTA). Render the same card in three contexts on the same page: a 280px sidebar, a 600px main column, and a full-width hero. Confirm the card adapts correctly in each context, all at the same viewport width.
Hint
Wrap each instance of the card in a div with container-type: inline-size. Inside the card's CSS, use @container queries to switch layouts at the chosen breakpoints. The card component itself doesn't need to know what context it's in — it just queries its container.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.