Update September 14, 2022: Safari 16 now also supports container queries!
Chrome 105 rolled out some pretty exciting CSS support updates including long-awaited container queries!
What are container queries?
Container queries are a new type of CSS query that, like many use cases for media queries, allows us to define styles based on a defined size. However, where @media
encompasses the width of the environment—viewport or user settings like zoom—the new @container
allows us to target specific elements on the page.
For a potential use case, let’s look at the related news stories section of an article on the New York Times’ website.
Here we have two columns: one wide column and one narrow column. The wider column includes a flex layout with multiple rows of three thumbnails and headlines (let’s call these headline and thumbnail sets “cards”). The narrower column includes a text-based list of links to the most popular stories. As I adjust the size of my browser window to be smaller, the two columns also change; the flex layout contained in the larger column reduces to show multiple rows of two cards instead of three.
(There’s an empty space in both of these screenshots because of my ad blocker. 🙃)
Currently, this is achieved with media queries. The individual cards are given a max-width
that increases or decreases depending on the viewport’s width in pixels.
.card {
max-width: 48%;
}
@media (min-width: 768px){
.card {
max-width: 32%;
}
}
This works fine here, but what if this flex container of cards had more applications? Maybe a content author could insert our flex container into both narrow and wide columns. Currently, we only have the one media query for a min-width: 768px
, but that would not be enough to make the cards look good in a significantly narrower column.
And just think if our imaginary stakeholders wanted even more options: one, three, four column layouts that have to fit our flex container. Our edge cases are going to quickly spiral out of control as we’ll have to manually test and write out every possible viewport width combination as a media query. Instead of doing that, though, we can use container queries to scope these variations to the width of the container itself.
.card-container {
container-type: inline-block;
}
.card {
max-width: 48%;
}
@container (min-width: 420px) {
.card {
max-width: 100%;
}
}
@container (min-width: 680px) {
.card {
max-width: 32%;
}
}
Note that we’re only styling the child .card
selector within the container query. @container
does not allow us to style the container itself. To apply responsive styles to the container element itself, we’ll need to still use @media
. Technically we could use another parent container, but if we need this flex container to have a variety of applications, nesting @container
queries would bring us back to the busy work of working out every possible edge case of parent component.
With the continued popularity of component-based design systems and UI component libraries like Storybook, container queries allow us to be more consistently modular and flexible in our code.
container-type
The key to getting a container query to work is adding a container-type
property to the element you wish to enable as a container. This will also allow the container to show up in Chrome DevTools.
In the latest working draft, container-type
can take three potential values:
-
normal
: the initial value/assumed default. This means the element will not be treated as a queryable container. -
size
: establishes the element as a query container for size queries on both the inline (horizontal) and block (vertical) axis. -
inline-size
: establishes the element as a query container for size queries on the inline (horizontal) axis.
inline-size
is the one you’ll probably see the most in early examples, and it’s the one I used in my codepen. It works the way you’re probably used to working with media queries where it is relative to the width of the container.
container-name
You may find yourself wanting to establish multiple containers on a page with different styles. In this case, you’ll want to use the container-name
property. This takes any string as a value and will allow you to specify which container you’re querying:
.card-container {
container-type: inline-block;
container-name: card-container
}
@container card-container (min-width: 420px) {
.card {
max-width: 100%;
}
}
In this example, I’ve used the class name as the container-name
, but that is by no means a requirement. It can be any unique, single-word string you need. As for best practices, while it’s still early days, the current draft uses kebob case (dashes) in its examples.
Size container features beyond width
In the examples above, I wrote my container queries using a min-width
and pixel value for my size feature, but we’re not limited to that format. Let’s look at this example from the working draft:
main {
container-type: size;
container-name: my-page-layout;
}
.my-component {
container-type: inline-size;
container-name: my-component-library;
}
@container my-page-layout (block-size > 12em) {
.card { margin-block: 2em; }
}
@container my-component-library (inline-size > 30em) {
.card { margin-inline: 2em; }
}
Recall that “inline” refers to the horizontal sizing while “block” is the vertical. In the example above, main
—named my-page-layout
—has the container-type of size
(both block and inline) while .my-component
—named my component-library
—has an inline-size
(just inline). Therefore, in their named container queries, @container my-page-layout (block-size > 12em)
will apply with a container height larger than 12em
, whereas @container my-component-library (inline-size > 30em)
will apply with a width larger than 30em
.
In future iterations, we may be to write size container queries in respect to aspect-ratio as well.
Limitations
As you may have guessed from multiple references to the working draft, @container
is not universally supported yet. It is available in only the newest version of Chrome and Chromium-based Edge (partially), but is not yet in FireFox or Safari. Keeping an eye on CanIUse.com, we see support is slowly but surely rolling out, as it’s now in the latest Safari Technology Preview. You will probably find yourself using @media
in most cases for a little while longer, but it’s exciting to see such mainstream progress on its adoption.
Posted late at night (for me) and edited with fresh eyes in the morning; cross-posted to my site.
Top comments (0)