Skip to main content

  • June 11, 2021

The Benefits of Container Queries

Decorative Illustration You may have heard that container queries are coming. Let’s take a quick look at what they are, why they are needed, and if you stick with me to the end a quick and simple container query solution that you can use today if you need them before they are ready for production.

On the web, media queries are one of the tools that help us create responsive websites. They allow us to change the style of an element based on some condition of its environment, usually the browser’s viewport width. For example, let’s say we have a card component that contains an image and some content. A media query would let us stack and center the image and content for browser widths under 420 pixels and display them horizontally at browser widths above that.

.card {
    display: flex;
    flex-direction: column;
    text-align: center;
}

@media (min-width: 420px) {
    .card {
        flex-direction: row;
        text-align: left;
    }
}

The process of building responsive websites that work well across all platforms using media queries to determine styles based on the browser’s width has served us well for a long time. However, as we seek to build more maintainable code and take steps to think about our builds in more modular ways, we need to recognize a fundamental flaw with that approach. While media queries are the right tool for many things, determining the style of a component by the browser’s width is almost never what you actually want.

In our previous example, we really wanted to stack the image and content when the container they were in was less than 420 pixels wide. But if the media query worked for us in this example, why do we need to do something different? Well, besides being more precise with the language we are using to describe and style our components, using a media query to style them can limit our components to specific contexts and makes it more difficult to achieve flexible and modular builds.

If we also wanted to display the card component in a two, three, or four column grid, the current code would be insufficient at certain browser widths. We would find ourselves having to add a growing list of styles for the different contexts at different breakpoints to try to achieve what we wanted, a card that stacks it’s contents when it’s under 420 pixels wide. It might look something like this:

.card {
    display: flex;
    flex-direction: column;
    text-align: center;
}
@media (min-width: 420px) {
    .card {
         flex-direction: row;
         text-align: left;
    }
    .w-1\/2 .card,
    .w-1\/3 .card,
    .w-1\/4 .card {
        flex-direction: column;
        text-align: center;
    } 
}
@media (min-width: 860px) {
    .w-1\/2 .card {
         flex-direction: row;
         text-align: left;
    }
}

This can quickly get out of hand. This will look different in your specific situation, of course, and the example is greatly simplified in that it assumes the grid columns themselves are not changing their composition responsively. But it illustrates the point that media queries can feel a bit clumsy when trying to style components that appear in multiple contexts. This example is a common one and one that I have encountered many times, but there are plenty of other examples where media queries feel inadequate. Andy Bell has a great example of how it’d be nice to be able to size text in relation to its container size. This is where container queries can help us out.

A container query will allow us to style a component based on the width of its container and not the viewport. While container queries have been a recommendation since 2015, we haven’t been able to use them in the browser until recently when chrome added them behind a developer flag. We can’t use them on production sites just yet, but that day will come thanks to Miriam Suzanne and other really talented people. Let’s take a quick look at how we will hopefully be implementing container queries in the future.

<div class="contain">
    <div class="card">
        ...
    </div>
</div>

.contain {
    contain: layout inline-size;
}
.card {
    display: flex;
    flex-direction: column;
    text-align: center;
}
@container (min-width: 420px) {
    .card {
        flex-direction: row;
        text-align: left;
    }
}

That’s it. Now this card will behave how we want in any context. Notice that the CSS is very similar to our original example but with two differences. We now have a direct parent div around our card that we need to define the contain style property on. Because of performance considerations, this effectively allows us to register the container so the browser will watch it. The other difference is that we changed from @media to @container. This is elegant and intuitive and I can’t wait to use it! If you have an immediate need for container queries on your project, though, I wrote up a quick solution using Alpine.js that you might find useful.

<div
    x-data="{width: $el.clientWidth}"
    x-on:resize.window.debounce="width = $el.clientWidth"
    :class="{
        'flex flex-col text-center': width < 420,
        'flex flex-row': width >= 420,
    }"
>
    <div>
        IMAGE
    </div>
    <div>
        CONTENT
    </div>
</div>

Alpine.js is an amazing little declarative framework that can help us create modular components for our templates. It’s also really easy to see what’s going on for this demo. On load, we set a width variable on our component to its width. We then update this if the browser is resized. Finally, we set our classes (I used Tailwind CSS for this example) based on that width. Note that there are performance implications to consider here as you could potentially be adding an excessive amount of resize events to the window, but we’re debouncing as well as relying on the probability that most users aren’t constantly resizing their browser while they navigate websites. If this is a concern, you should definitely check out Philip Walton’s elegant solution that uses ResizeObserver to achieve effective container queries.

I hope you now see the benefits of using container queries and have a good example you can use if you need to implement them today. I encourage you to explore some of the resources I linked to in this article if you are interested in learning more.

Back to Top

comments powered by Disqus