Skip to main
Article
Wireframe of card elements
in different sized containers --
some laid out vertically in small spaces,
others horizontal when there's room.

Use the Right Container Query Syntax

Size queries are stable, and shipping in browsers

Since we got a first look at a Container Queries prototype back in April 2021, the syntax has changed a few times. But now the spec is stable, browsers are getting ready to ship, and it’s time to make sure you’re using the same syntax they are!

Update :
  • Safari 16 shipped on September 12, with support for size Container Queries and units (and much more).
Update :
  • Older versions of Safari Technology Preview and Chrome both had a bug requiring parentheses around queries with logical operators (not, and, or). Upgrade to Chrome 105 or Safari 16 to get the proper behavior.
Update :
  • Chrome 105 shipped on August 30, with support for size Container Queries and units.

Container Queries allow us to measure (or ‘query’) aspects of a ‘container’ element, and style any descendants based on the result of the query. This is very similar to Media Queries, which allow us to query the overall browser viewport.

If you haven’t encountered Container Queries before, there are many resources available on the web. David Herron wrote a great Quick Start Guide back in April 2021, when Chrome Canary shipped the first prototype of the feature.

Since then the syntax has gone through several revisions. That’s why we prototype – it allows us to learn and make changes, before developers are relying on the feature in production. The downside is, many people (including me!) have written articles, made demos, given talks, and released videos using now-out-of-date syntax.

Now Safari and Chrome have both signaled that they are ready to ship Container size queries and units, likely starting in late August 2022.

So, what syntax is actually shipping in browsers?

main, .sidebar {
  /* establish containers for inline-size queries */
  container-type: inline-size;
}

main {
  /* optionally give a container one or more names */
  /* - use any names you want, there are no pre-defined options */
  container-name: main-content page-layout;
}

article {
  /* a shorthand to set both names and types */
  /* - names are required and come first in the shorthand */
  container: article-layout / inline-size;
}

/* query the nearest ancestor container named page-layout */
/* - using a container-name is optional in the query */
/* - we can also use the old `(min-width: 40em)` syntax */
@container page-layout (inline-size > 40em) {
  .card {
    grid-template-columns: auto 1fr;
  }
}

h2 {
  /* container-query-inline units */
  font-size: max(1.2em, 1em + 2cqi);
}

If you are looking at an old demo, talk, or article, and wondering why it doesn’t work, there are two major changes that you should check first:

Let’s dig into the details…

In order to query elements in the page, we need to define them as query containers. We can do that with the container-type property, which defines the questions we’re able to ask about a given container.

The initial value is normal. By default, all elements are style containers – meaning we should be able to query the computed value of any property on any element. But don’t worry about that yet, browsers are shipping size queries and units first.[1] For now, you can use the normal value to override other values – similar to using the initial keyword.

Size containers need to be defined explicitly, because they require special size containment in order to function. Normally, the size of an element would be based on the size of its contents – but if we query that size, and change the contents based on the query, we have an infinite loop. Size containment breaks that loop by ensuring the size of a container is not based on the size of its contents.

Here we have two options:

There is no block-size option. I won’t go into all the reasons here, but it wasn’t possible for browsers to implement, and there are fewer use-cases for it anyway.

Over time, Media Queries have expanded to cover much more than viewport sizing – we can query user preferences, display quality, color-depth, interaction capabilities, and more. We expect a similar expansion with Container Queries. Size queries will ship this summer, but style queries are already well-defined, and ready for browsers to implement. We’re also looking into a range of possible state queries – for example, selecting elements inside a position: sticky container based on the stuck or unstuck ‘state’ of that container.

By default, a container query will look for the nearest ancestor container of the appropriate type. So if we ask about the inline-size of a container, we’ll get the inline dimensions of the nearest ancestor with a size-based container-type. That’s a handy default, but it’s more resilient if we can name our containers, and know that we’re measuring the right thing.

We can do that with the container-name property. It allows us to give a container any number of custom names that we want. Those names can be any valid CSS ‘custom-ident’ that we come up with (similar to naming keyframe animations, grid-areas, layers, etc), and they are not required to be unique. There are only a few reserved words – like none or not or initial – that we can’t use as names.

Think of container-names more like a ‘class’ than an ‘ID’. We can make them as broad or specific as we need. I think it’s useful to establish some broad categories like layout or component – and then occasionally add more detailed/specific identifiers as needed (like main in the code sample above).

There’s also a container shorthand that allows us to define both the container types and names in a single property. This should only be used to set both the name AND type of a property:

main, .sidebar {
  /* <name> comes before the slash, <type> comes after */
  container: page-layout / inline-size;
}
Note:

Names go first, before the slash. This is one of the big changes that will break a lot of demos. In an earlier draft of the feature, the order was reversed. If you see an old container query article or talk or demo that no longer works, this is probably why.

Did you know that Container Queries also come with their own units? These work the same as viewport units (vw, vh, vmin, etc) – but if they end up inside a size container, they’ll use the container dimensions instead of the viewport dimensions. If they aren’t inside a container, they’ll use the ‘small viewport’ dimensions.

Note:

This also changed after some of the early demos came out. The original prototype used q* instead of cq* as the unit prefix.

Scott Kellum has a great demo on Codepen:

See the Pen Container units by @scottkellum on CodePen.

Now that we have some (size) containers defined, we can query various aspects of their dimensions:[2]

@container (inline-size > 40em) {
  h2 { font-size: 1.5em; }
}

@container page-layout (min-width: 35em) {
  .card {
    grid-template-columns: auto 1fr;
  }
}

There are a few things to note here:

Chrome 105 & Safari 16 both support size queries and units. Firefox is actively working on support, but hasn’t yet announced when it will be ready. Here’s the current support for each feature:

Data on support for the css-container-queries feature across the major browsers from caniuse.com

Data on support for the css-container-query-units feature across the major browsers from caniuse.com


  1. Chrome has started to implement a prototype of style queries (for custom properties only) in Chrome Canary, using the --enable-blink-test-features runtime (command line) flag. Keep an eye out for more details soon! ↩︎

  2. Check out the spec for a full list of size container features that can be queried – like aspect-ratio and orientation. ↩︎

Mia from behind,
standing at a laptop -
speaking to a conference audience
and gesturing to one side

Cascading Style Systems

A workshop on resilient & maintainable CSS

New CSS features are shipping at an unprecedented rate – cascade layers, container queries, the :has() selector, subgrid, nesting, and so much more. It’s a good time to step back and understand how these tools fit together in a declarative system – a resilient cascade of styles.

Register for the October workshop »

Webmentions

Dean Leigh

from twitter.com

You are a star, thank you. I stopped for a while because I ran into syntax changes a few times and started getting confused. Looks like it’s time to open those old codepens and dive back in.

OddBird

from twitter.com

Makes sense. It’s the difficult balance of a prototype – we’re doing it to learn & get feedback & make changes, but it can also lead to confusion. But yes - it’s stable & safe now! Welcome back. :)

Dean Leigh

from twitter.com

Completely understand, whether the wait. I turned my attention to combining Sass and Custom Properties which was rabbit hole more than deep enough to keep me busy and ended up recently reading your article on Proximity so I owe you thanks again 🙏

Adrian Roselli 🗯

from twitter.com

Welp, guess I had better learn container queries. Here I was thinking I could code things to XHTML 1.0 Strict and CSS 1.0 until I died. And since I would rather go gaming than to a dirt nap… I will read it during gaming.

Recent Articles

  1. A gallery of numbered images in four columns
    Article post type

    Choosing a Masonry Syntax in CSS

    What makes something a ‘grid’, and what’s at stake?

    Back in 2020, Firefox released a prototype for doing ‘masonry’ layout in CSS. Now all the browsers are eager to ship something, but there’s a hot debate about the best syntax to use.

    see all Article posts
  2. see all Article posts
  3. Article post type

    Partial Feature Queries, Relaxed Layout Containment, and More

    CSS Working Group updates from July

    Over the last month, the CSS Working Group has determined we can loosen containment restrictions for query containers, and agreed on a syntax for special-case support queries (like support for the gap property in a flex context, or support for align-content in a block flow context).

    see all Article posts