Containers, image by Frank mckenna. Unsplash

I’ve been hyped about the new container queries in CSS. The possibility to change the look & feel of elements based on the location they are presented instead of the viewport width will be a great addition to the front-end workflow.

It all started with a proposal by a few people including Miriam Suzanne who drew the first ideas into how containment should behave. With a lot of upvotes on the topic and more people backing it as a highly-needed feature, it’s finally available with a flag on Chrome Canary with many more browsers to follow soon.

We’ve all been there, you have a beautifully styled call to action in the main content section of a website, after some A/B testing or client feedback, there is a decision that the call to action would be better off in the sidebar. Most of the time we start adding some classnames to the card or its parent and change their look based on the media queries. It does the trick, we’ve done it for some while now, but what if we can make all of this a little bit more dynamic, readable and maintainable.

I created a little example to illustrate the mass use of media queries to get to a certain result. I'm pretty sure I could write this example with less code, but if we’re honest, when working with deadlines and project rushing, this isn’t far away from the truth.

How will container queries with CSS help us?

First of all, if you want to view the demo’s provided in this blogpost, you will have to use chrome canary for now, go to chrome://flags and search for Enable CSS Container Queries and enable it.

Rather than relying on the viewport, we will be able to change the behaviour of our component based on the way it’s contained. This basically means that we won’t have to provide extra classes and will be able to style component based, rather than viewport based. In short: CSS container queries will add many benefits to the fluid web.

How does it all work?

First of all we’ll need to specify the container for the parent(s) of our elements. This is done with the container property. The container property is a shorthand for the following:

container-type
Defines an element as a query container. Descendents can query aspects of its sizing, layout, and style.

container-name
Specifies a list of query container names for @container rules to use to filter which query containers are targeted.

The container-type can have the following values:

inline-size
Establishes a query container for dimensional queries on the inline axis of the container. Applies layout, style, and inline-size containment to the element.

block-size
Establishes a query container for dimensional queries on the the block axis of the container. Applies layout, style, and block-size containment to the element.

style
Establishes a query container for style queries.

state
Establishes a query container for state queries.

More information on this can be found on the MDN Web Docs.

Basic example of the container property.

Let’s say we want to style a card differently based on the width of its current container. In this example our sidebar is at least 400px wide and we want to style the card in a horizontal grid when it’s bigger than the sidebar’s width. We want to create the following:

The container query demo screenshot

So the HTML might look something like this:

<section class="grid container">
  <main class="content">
    <article class="card">
      <img src="https://picsum.photos/800/600?random=1" alt="" />
      <div class="card-body">
        <h2>Some rondom title in here</h2>
        <p class="card-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis sodales erat vel accumsan.</p>
        <a href="">Read more</a>
      </div>
    </article>
    <article class="card">
      <img src="https://picsum.photos/800/600?random=2" alt="" />
      <div class="card-body">
        <h2>Some rondom title in here</h2>
        <p class="card-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis sodales erat vel accumsan.</p>
        <a href="">Read more</a>
      </div>
    </article>
  </main>
  <aside class="sidebar">
    <article class="card">
      <img src="https://picsum.photos/800/600?random=3" alt="" />
      <div class="card-body">
        <h2>Some rondom title in here</h2>
        <p class="card-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis sodales erat vel accumsan.</p>
        <a href="">Read more</a>
      </div>
    </article>
  </aside>
</section>

And then we handle the CSS container query:

/* Basic card styling */
.card {
  margin-bottom: 24px;
  border: 1px solid #444;
  background: rgba(25,35,51);
}

.card-body {
  padding: 15px;
}

.grid > * {
  container-type: inline-size;
}

@container size(min-width: 401px) { 
  .card {
    display: grid;
    gap: 15px;
    grid-template-columns: 100px 1fr;
    padding: 15px;
    align-items: center;
  }
  
  .card-body {
    padding: 0;
  }
}

All done! You can view the full example on codepen (where I went a bit more in-depth).

But what if we just want this behaviour only in the content section of the page?

That’s where the container-name comes into play. I duplicated the example above and added the following update to the css:

.content {
  container-name: content;
}

@container content size(min-width: 401px) { 
	/* Same code as before here */
}

You can view a demo of container-name for container queries here.

Inline-size vs block-size containment.

In the example above, I used the inline-size because I only needed to change the behaviour based on the container’s width. It will be possible to use block-size as well, so that we can change the behaviour based on the height. Unfortunately, this is not yet supported. But the possibilities of this will be amazing. I already created a little template to start working on it as soon as it’s available.

Benefits of “contain” with content provided by a CMS.

When writing and experimenting with new CSS features I always try to imagine real-life problems I had to deal with in the past. And this one is very common and something that others have experienced as well.

The case: When creating a homepage for a client, there might be a “in the picture” section. The client is allowed to add up to three items in the CMS. But from time to time they just want to add one big banner, or maybe two items. When this happens, we mostly rely on counting the items (through PHP or JS maybe?) so that we can add different class names based on the length of an array. Sounds familiar?

By using container queries such as the following we can handle this behaviour perfectly without relying on extra class names by providing a fluid CSS grid in combination with CSS container queries:

<!-- When the CMS gives 2 items as an output (feel free to change the amount of items) -->
<h2 class="subtitle">CMS outputs 2 items</h2>
<section class="container grid-cta">
  <div class="grid-cta-item">
    <article class="card-cta">
      <img src="https://picsum.photos/1600/900?random=1" alt="" />
      <div class="card-body">
        <h2>Call to action nr 1</h2>
        <p class="card-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis sodales erat vel accumsan.</p>
        <a href="">Read more</a>
      </div>
    </article>
  </div>
  <div class="grid-cta-item">
    <article class="card-cta">
      <img src="https://picsum.photos/1600/900?random=2" alt="" />
      <div class="card-body">
        <h2>Call to action nr 2</h2>
        <p class="card-description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras convallis sodales erat vel accumsan.</p>
        <a href="">Read more</a>
      </div>
    </article>
  </div>
</section>
/* basic styling of our card */
.card-cta {
  margin-bottom: 24px;
  border: 1px solid #444;
  background: rgba(25,35,51);
}

.card-body {
  padding: 1.4rem;
}

/* Setting up our cta grid */
.grid-cta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  grid-gap: 1.4rem;
}

.grid-cta-item {
  container-type: inline-size;
}

/* change style of our cards based on their container width */
@container size(min-width: 560px) { 
  .card-cta {
    display: grid;
    gap: 1.4rem;
    grid-template-columns: 215px 1fr;
    padding: 1.4rem;
  }
  
  .card-body {
    padding: 0;
  }
}

@container size(min-width: 700px) { 
  .card-cta {
    position: relative;
    display: block;
    padding: 0;
    border: 0;
  }
  
  .card-cta::after {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, .2);
    z-index: 1;
    content: '';
  }
  
  .card-body {
    position: absolute;
    top: 50%;
    left: 50%;
    max-width: 500px;
    text-align: center;
    transform: translate(-50%, -50%);
    z-index: 2;
  }
}

This really shows the strength of the container queries. I added this example on codepen as well. You can find it here.

The end results gives us something like this:

Three cards in a row with grid and container queries
The CMS provides 3 items.
Full width card example with container queries
The CMS provides only one item.

Using container queries with polyfills.

Yes, you can start using this awesome feature right now with a polyfill. Personally I would advice using it with PostCSS. I won’t go in-depth on this because it’s very well documented. However I might add a demo of this on github in the near future.

Check out the polyfill for container queries here.

Are container queries performant?

Since this is an experimental feature, there isn’t a final metric on this. However, there are certain benchmarks on which these new features are tested and as many more browsers are starting the development of it, I’m pretty sure it won’t be a problem.

Further experimentation and reads.

There are many codepens already showing this feature and even I can’t help myself adding a little experiment. Such as this 5-frame animation based on container width (Really, don’t use this kind of thing in your live projects, it’s just me having a bit of fun with CSS).

Screenshot of a CSS animation based on container queries

Check out this bird animation with container queries on codepen.

Want to go more in depth? Here are some reads for you:

Note: I updated the examples on january 12, 2022 with the new specification

 in  CSS