Complete CSS Course Module 15: Advanced: Modern CSS Features

CSS Container Queries: Responsive Components

  • Beginner
  • Lesson 15.2
  • 4 min read
  • 64 of 77 in the course

In short

CSS container queries let a component change its layout based on the size of its container, not the whole screen. They make truly reusable responsive components. Why do we need container queries? Media queries look at the size of the browser window. That works for page layouts.

CSS container queries let a component change its layout based on the size of its container, not the whole screen. They make truly reusable responsive components.

Why do we need container queries?

Media queries look at the size of the browser window. That works for page layouts. But a component, like a card, does not know where it will live. The same card might sit in a wide main column or a narrow sidebar on the same screen.

With media queries, the card on a big screen thinks "I have lots of room" even when it is squeezed into a 250px sidebar. Container queries fix this. The card asks, "How wide is my box?" instead of "How wide is the screen?"

Think of a person choosing clothes. A media query checks the weather report for the whole country. A container query looks out of the window of the room you are in.

How to use container-type and @container

You need two steps:

  1. Mark a parent element as a container with container-type: inline-size. "Inline size" means the width, in normal left-to-right text.
  2. Write @container rules for the children.
.card-wrapper {
  container-type: inline-size;
}

@container (min-width: 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
}

The .card becomes a flex row only when .card-wrapper is at least 400px wide. The screen size does not matter.

Common mistake: Putting container-type on the same element you want to restyle. A container query can only style the children of the container, not the container itself. That is why we add a wrapper.

You can also give a container a name with container-name, or use the shorthand container: card / inline-size;. Then @container card (min-width: 400px) only checks that named container, which is useful when containers are nested.

Example: a card that adapts to its column

Here is the same card used in a wide main area and in a narrow sidebar:

<div class="layout">
  <main>
    <div class="card-wrapper">
      <article class="card">
        <img src="https://picsum.photos/400/300" alt="Fresh salad bowl">
        <div class="card__body">
          <h3>Green summer salad</h3>
          <p>A fresh, easy salad ready in 10 minutes.</p>
        </div>
      </article>
    </div>
  </main>
  <aside>
    <div class="card-wrapper">
      <article class="card">
        <img src="https://picsum.photos/400/301" alt="Tomato soup">
        <div class="card__body">
          <h3>Tomato soup</h3>
          <p>Warm and simple comfort food.</p>
        </div>
      </article>
    </div>
  </aside>
</div>
.layout {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 2rem;
  padding: 2rem;
  font-family: system-ui, sans-serif;
}

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

.card {
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  overflow: hidden;
  background: white;
}

.card img {
  width: 100%;
  display: block;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.card__body {
  padding: 1rem;
}

.card h3 {
  margin: 0 0 0.5rem;
  font-size: clamp(1.1rem, 4cqi, 1.6rem);
}

@container (min-width: 450px) {
  .card {
    display: grid;
    grid-template-columns: 40% 1fr;
    align-items: center;
  }

  .card img {
    height: 100%;
  }
}

In the wide main column, the salad card shows the image on the left and the text on the right. In the narrow sidebar, the soup card stacks the image on top. It is the same HTML and the same CSS, only the container size is different.

What are container query units like cqi?

Container queries come with their own units. They are like vw, but measured against the container instead of the viewport:

  • cqi: 1% of the container's inline size (width).
  • cqb: 1% of the container's block size (height).
  • cqmin and cqmax: the smaller or larger of the two.

In the example above, font-size: clamp(1.1rem, 4cqi, 1.6rem) makes the heading grow with the card, not with the screen. A card in a sidebar gets a smaller heading than the same card in the main area.

Note: cqi units need an ancestor with a container-type. If there is none, they fall back to the viewport size, like vw.

Container queries vs media queries: which should you use?

You need both. They answer different questions.

Media queriesContainer queries
What they measureThe browser windowA parent element
Best forPage layout: columns, header, navigationComponents: cards, widgets, forms
Rule@media (min-width: 800px)@container (min-width: 400px)

A good habit: use media queries to decide how many columns the page has, and container queries to decide how each component looks inside its column. Container queries work in all modern browsers.

Practice exercise

Create a "user profile" component with an avatar, a name and a short bio. Put one copy in a 300px wide box and another in a 700px wide box. Using a container query, make the wide version show the avatar next to the text, and the narrow version stack them and center the text.

Hint: Wrap each profile in an element with container-type: inline-size, and set the box widths on those wrappers.

Key takeaways

  • Container queries respond to the size of a parent, not the screen.
  • Add container-type: inline-size to a wrapper, then use @container for its children.
  • A container cannot style itself with its own query, so use a wrapper element.
  • cqi is 1% of the container's width and is great for fluid component text.
  • Use media queries for page layout and container queries for components.