EduFlow
Modern CSS Mastery
0%

Section 2 · Layout That Holds Up

Container Queries

20 min3 min readCode playground

Lesson at a glance

Make a session card change its layout based on the width of the slot it sits in, using size container queries, container units and custom-property style queries.

  1. 1Make a container, then query it
  2. 2The two rules that trip everyone
  3. 3Container units
  4. 4Style queries for variants
  5. 5Your turn

No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.

Waypoint uses the same session card in three places: the main schedule grid, a narrow “Up next” sidebar, and a full-width hero for the keynote. A media query can only ask “how wide is the screen?”, and the screen is the same width in all three cases. So the card in the sidebar got the wide layout, squashed into 280px, until someone added a .sidebar .session-card override, then a .hero .session-card override, and so on.

The card shouldn’t care where it is. It should care how much room it has. Container queries let it ask exactly that.

Make a container, then query it

Two steps. First, declare which element is the container, the slot the card sits in:

slots.css
.slot {
  container: session / inline-size;
}

That shorthand sets container-name: session and container-type: inline-size. The type tells the browser you’ll query this element’s inline size (its width in horizontal writing), so it must be able to calculate that width without looking at the content inside.

Second, write the card’s layout as a query against that container:

session-card.css
.session-card {
  display: block;
}

@container session (width >= 400px) {
  .session-card {
    display: grid;
    grid-template-columns: 6rem 1fr;
    gap: 1rem;
  }
}

Now the card stacks in the 280px sidebar and switches to two columns in the 640px grid, with no knowledge of either. The name is optional; an unnamed @container (width >= 400px) uses the nearest ancestor with a size container type. Names help when containers nest and you want to skip one.

A browser window containing a narrow sidebar slot and a wide main slot. The card in the narrow slot is stacked: time above title. The card in the wide slot is side by side: time column next to the title. viewport: same for both .slot 280px 09:30 Title .slot 640px 09:30 Title
The same card, two slots: the query measures the slot, not the viewport

The two rules that trip everyone

An element can’t query itself. The query looks for an ancestor container, so container-type on .session-card plus @container { .session-card { … } } does nothing for the card itself. Put the container on a wrapper.

Containment changes how the container is sized. inline-size containment means the container’s width can’t depend on its children; otherwise a query could change the content, which changes the width, which changes the query, forever.

Container units

Inside a container, you get units relative to it: cqi is 1% of the container’s inline size, cqb 1% of its block size, and cqmin/cqmax the smaller or larger of the two. They make a component scale smoothly, not just at breakpoints:

CSS
.session-card h3 {
  font-size: clamp(1rem, 0.75rem + 2cqi, 1.5rem);
}

In the 280px sidebar the title sits near 1rem; in a 640px slot it grows towards 1.5rem. If no ancestor is a size container, container units fall back to the small viewport units, so they never break.

Style queries for variants

Size isn’t the only thing a card might respond to. The keynote card in the hero is a variant, and container style queries let a parent announce that through a custom property:

CSS
.hero { --variant: featured; }

@container style(--variant: featured) {
  .session-card {
    border-inline-start: 6px solid #6d28d9;
  }
}

You don’t need container-type for this: every element is a style container by default, so the query checks the nearest ancestor’s computed --variant. Today, style queries work with custom properties only; querying regular properties like style(display: grid) isn’t supported anywhere yet.

Your turn

In the exercise, the card uses a media query, so it gets the wrong layout in at least one slot. Turn .slot into a named container, switch the layout to a container query at 400px, and add a style query for the featured variant. The checks change the slot’s width and set --variant on it to watch the card respond. That closes the layout section; next you’ll scale type and spacing fluidly across all those slot sizes.

Key takeaways

  • container-type: inline-size turns an element into a container, and @container (width >= 400px) styles its descendants based on that element’s width, not the viewport’s.
  • An element can’t query itself; put container-type on a wrapper and write the query for the elements inside it.
  • Inline-size containment means the container’s width can’t come from its content, so a container in a shrink-to-fit context collapses unless something gives it a width.
  • Container units such as cqi (1% of the container’s inline size) let type and spacing scale with the component.
  • Style queries on custom properties, such as @container style(--variant: featured), became Baseline in May 2026; size queries have been Baseline since 2023.

Further reading

Check your understanding

4 questions

Question 1 of 4

Why does this rule never apply?

CSS
.session-card { container-type: inline-size; }
@container (width >= 400px) {
  .session-card { display: grid; }
}
Question 2 of 4

You put container-type: inline-size on a flex item that has no width set, and it collapses to zero width. Why?

Question 3 of 4

A card’s title has font-size: clamp(1rem, 4cqi, 1.5rem). What does 4cqi refer to?

Question 4 of 4

Which query applies styles when a parent sets --variant: featured?

Practice

Code playground

Let the card measure its slot

The card switches to two columns with a media query, so it measures the screen instead of its slot. The checks set .slot to widths between 300px and 500px.

  1. Make .slot a size container named session that tracks its inline size.
  2. Replace the media query with a container query: when the session container is 400px or wider, the card becomes the two-column grid; below that, it stays stacked.
  3. Add a style query: when --variant is featured, the card’s inline-start border becomes #6d28d9.
<div class="slot">
  <article class="session-card">
    <p class="time">09:30</p>
    <div class="body">
      <h3>Container Queries in Production</h3>
      <p>Cards that adapt to the slot they're in, not the screen.</p>
    </div>
  </article>
</div>Loading the editor…

Ctrl/⌘ + Enter to run · Runs in a sandbox in your browser. Nothing leaves your device.

Preview

Console

(no output)

Checks

  • The slot is an inline-size container named session
  • At 500px the time sits beside the content
  • At 300px the card stacks
  • The switch happens exactly at 400px
  • –variant: featured turns the border violet