Container Queries: Styling Elements by Parent, Not Viewport

Chrome is currently experimenting with @container, a property being developed within the CSS Working Group's Containment Level 3 spec. Championed by Miriam Suzanne of Oddbird alongside a group of engineers across the web platform, @container introduces the ability to style elements based on the size of their parent container, rather than the viewport alone.

The @container API is not stable, and is subject to syntax changes. If you try it out on your own, you may encounter a few bugs. Please report those bugs to the appropriate browser engine: Chrome | Firefox | Safari

Think of container queries as media queries (@media), but instead of relying on the viewport to adjust styles, the parent container of the element you're targeting drives the changes. This represents a significant shift in how responsive design works: elements will no longer depend solely on the viewport and user agent. With container queries, the same element placed in a sidebar, body, or hero can look completely different based on its available size and dynamics.

Getting Started with @container

To use @container, you first need to create a parent element that has containment. This requires setting contain: layout inline-size on the parent. Currently, container queries can only apply to the inline axis, which prevents layouts from breaking in the block direction. Setting this property creates a new containing block and a new block formatting context, letting the browser separate it from the rest of the layout.

In the following example, two cards are placed inside a parent with this markup:

<div class="card-container">
  <div class="card">
    <figure> ... </figure>
    <div>
      <div class="meta">
        <h2>...</h2>
        <span class="time">...</span>
      </div>
      <div class="notes">
        <p class="desc">...</p>
        <div class="links">...</div>
      </div>
      <button>...</button>
    </div>
  </div>
</div>

Containment is set on the parent (.card-container) that will be queried for container styles. A relative grid layout is applied to the parent of .card-container, so its inline-size changes based on that grid—this is what @container queries:

.card-container {
  container-type: inline-size;
  width: 100%;
}

Once containment is established, you can query for container styles to adjust the layout. This works similarly to width-based media queries: use max-width to apply styles when an element is smaller than a given size, and min-width when it is larger.

/* when the parent container is smaller than 850px, 
remove the .links div and decrease the font size on 
the episode time marker */

@container (max-width: 850px) {
  .links {
    display: none;
  }

  .time {
    font-size: 1.25rem;
  }

  /* ... */
}

/* when the parent container is smaller than 650px, 
decrease the .card element's grid gap to 1rem */

@container (max-width: 650px) {
  .card {
    gap: 1rem;
  }

  /* ... */
}

Micro Layouts Meet Macro Layouts

A standout feature of container queries is the ability to separate micro layouts from macro layouts. Individual elements can be styled with container queries for nuanced, component-level responsiveness, while full page layouts continue to be handled by media queries. This layered control enables more finely tuned interfaces—for example, a calendar UI can shift from a single-panel to a multi-panel display via media queries, while container queries handle the date layout, sizes, and event margins within each panel.

Pairing Container Queries with CSS Grid

Container queries become especially powerful when combined with CSS grid. Consider a plant commerce UI built entirely without media queries—only container queries and CSS grid drive the responsive behavior. The product grid uses:

grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));

This creates a layout where cards take up available fractional space until they reach 230px, at which point they flow to the next row. A container query then adjusts each card's internal layout: cards less than 350px wide adopt a vertical block layout, while wider cards shift to a horizontal inline layout by applying display: flex (which defaults to an inline flow).

@container (min-width: 350px) {
  .product-container {
    padding: 0.5rem 0 0;
    display: flex;
  }

  /* ... */
}

This means each card owns its own responsive styling—the product grid handles the macro layout, while the product cards manage their own micro layout.

Current Limitations

Height-based container queries are not yet supported—only the inline axis can be queried. Additionally, grid children require a wrapper element to work with @container. Despite this constraint, adding a wrapper still lets you achieve the desired effects.

Experimentation

To try the @container property in Chromium today, navigate to chrome://flags in Chrome Canary and enable the #experimental-container-queries flag.