CSS Container Queries

Updated Aug 31, 2021#css#guides

Container queries is a proposal that would allow web developers to style DOM elements based on the size of a containing element rather than the size of the browser viewport.

This way, you could create a component that is styled one way when it’s in a narrow container (like a sidebar), but another way when it’s got some breathing room (like in the main content of the page), without having to manually specify what kinds of containers are each type - it just naturally happens due to the width of the respective containers.

Motivation

Given a complex responsive layout, developers often require granular control over how the contents of an individual module will respond relative to the size of their parent container rather than the viewport size.

This limitation conflicts with the goal of creating modular, independent components, often requiring a number of redundant CSS, complex exception cases, and workarounds, and the problem compounds itself depending on how dramatically a module adapts at each of its breakpoints.

Writing modular code is about making small objects, and making them self-contained. Media queries don’t let you do that. In most cases, you don’t actually care about the width of the entire document or screen, you care about the width of your element.

Media queries are relative to your screen, so every time you write a media query for max-width or min-width, you’re connecting the appearance of your module to the width of the entire canvas—exactly what you were trying to avoid.

Container queries is the biggest improvement to CSS since Grid. It opens up all sorts of elegant solutions to problems we work around on a daily basis.

No longer will the viewport and user agent be the only targets we have to create responsive layout. One of the best features of container queries is the ability to separate micro layouts from macro layouts.

You can style individual elements with container queries, creating nuanced micro layouts, and style entire page layouts with media queries, the macro layout. This creates a new level of control that enables even more responsive interfaces.

This makes the various components that we might create in our pattern library truly reusable, without us needing to know the context that they are in.

Many container query cases can be solved, or at least partially solved, with existing technology. These solutions will remain important even as container queries make it into browsers, as fallbacks for those browsers that don’t yet support the spec.

Implementation

Implementing container queries is hard to do in CSS and browser vendors have been hesitant to implement them. Many proposals & Javascript implementations use a pseudo-class or switch() function.

/* pseudo-class */
.selector:container(<query >) {
  /* ... */
}

The most adopted proposal is using the contain property and @container at-rule, will be included in CSS Containment Module Level 3.

<section class="card-grid">
  <div class="card-container"><div class="card">...</div></div>
  <div class="card-container"><div class="card">...</div></div>
  <div class="card-container"><div class="card">...</div></div>
  <div class="card-container"><div class="card">...</div></div>
</section>
/* the outer element can get containment… */
.card-container {
  contain: layout inline-size;
}

/* which gives .card something to query against */
@container (inline-size > 30em) {
  .card {
    grid-template: 'image content' 1fr 'image footer' auto / 1fr 3fr;
  }
}

At the time of writing, CSS container queries are only supported in Chrome Canary enabled with the “Enable CSS Container Queries” feature flag under chrome://flags.

The @container at-rule provides several advantages:

  • The at-rule syntax matches more closely with existing conditional rules, and builds on existing query-list syntax.
  • It’s likely that a responsive component will have multiple moving parts, and each might require unique selectors based on the same query. These can be grouped in an at-rule.
  • We avoid the issues mentioned above with having an explicit selector attached to the query.

This can happen at multiple levels of layout, even inside nested components. The important distinction is that the container is distinct from the component being styled. Authors can query one to style the other.