Container queries in CSS

As a front-end designer, I haven’t been as excited about a new CSS feature over the past 6 years as I am now. Through the efforts Miriam Suzanne and other smart people prototype container requests can be enabled in Chrome Canary

There are a lot of jokes about container requests, but they are finally here. In this article, I’ll explain why we need container queries, how they make our lives easier, and most importantly, your components and layouts will become more powerful. If you’re as excited as I am, let’s dive into the topic. You are ready?


The problem with media queries

The web page is made up of different sections and components, which we make responsive using CSS media queries. There is nothing wrong with that, but there are limitations. For example, we can write a media query to show the minimum version of a component on a mobile or desktop computer. Often times, responsive web design has nothing to do with the viewport or screen size. It deals with the size of the container. Consider this example:

Here’s a very typical layout with a card component. And two options:

  • Stack (see aside).

  • Horizontally (see main).

There are several ways to implement this in CSS, here is the most common one: we need to create base component and then write variations of it.

.c-article {
  /* The default, stacked version */
}

.c-article > * + * {
  margin-top: 1rem;
}

/* The horizontal version */
@media (min-width: 46rem) {
  .c-article--horizontal {
    display: flex;
    flex-wrap: wrap;
  }

  .c-article > * + * {
    margin-top: 0;
  }

  .c-article__thumb {
    margin-right: 1rem;
  }
}

Note that we have described the .c-article – horizontal class to work with the horizontal version of the component. If the viewport is wider 46rem, the component should switch to the horizontal version. It’s not bad, but it somehow makes you feel limited. I would like the component to react to the width of its parent componentrather than the browser viewport or screen size.

Consider that we want to use the standard .cc-card in the main section. What’s going to happen? Well, the map will expand to the width of its parent, and therefore too large. Take a look at the picture:

This is a problem and we can solve it with container queries. (Yes! Finally!) Before diving into the topic, let’s take a look at the result.

We need to tell the component that if the immediate width of the parent is greater than 400px, it needs to switch to a horizontal style. The CSS would look like this:

<div class="o-grid">
  <div class="o-grid__item">
    <article class="c-article">
      <!-- content -->
    </article>
  </div>
  <div class="o-grid__item">
    <article class="c-article">
      <!-- content -->
    </article>
  </div>
</div>
.o-grid__item {
  contain: layout inline-size;
}

.c-article {
  /* The default style */
}

@container (min-width: 400px) {
  .c-article {
    /* The styles that will make the article horizontal**
        ** instead of a card style.. */
  }
}

How will container requests help?

Warning: CSS container queries are currently only supported in Chrome Canary with an experimental flag.

With CSS container queries, we can solve the above problem and create a stretchable component. This means that we can nest a component in a narrow parent, and it will become a stacked version, and in a wide parent, a horizontal version. Again, all elements are independent of the viewport width.

This is how I imagine it:

The purple outline is the width of the parent component. Notice how the component adapts to the larger size of its parent component. Isn’t that awesome? Here’s the power of CSS container queries.

How container requests work

You can now experiment with container queries in Chrome Canary. To enable them, go to chrome: // flags, find the “container queries” checkbox and check it.

First, let’s add the contain property. The component will adapt to its parent width, so you need to tell the browser not to redraw the entire page, but only its variable area. We can inform the browser about this in advance using the contain property.

The inline-size value means that the component only reacts to changes in the width of the parent element [прим. перев. — в случае языков с вертикальным направлением, возможно, речь идёт о высоте]… I tried using block-size, but this property doesn’t work yet. Please correct me if I am wrong.

<div class="o-grid">
  <div class="o-grid__item">
    <article class="c-article">
      <!-- content -->
    </article>
  </div>
  <div class="o-grid__item">
    <article class="c-article">
      <!-- content -->
    </article>
  </div>
  <!-- other articles.. -->
</div>
.o-grid__item {
  contain: layout inline-size;
}

This is the first step. We have defined the .o-grid__item element as the parent of .c-article. The next step is to add the styles you want to make the container queries work.

.o-grid__item {
  contain: layout inline-size;
}

@container (min-width: 400px) {
  .c-article {
    display: flex;
    flex-wrap: wrap;
  }

  /* other CSS.. */
}

@container is a .o-grid__item element and min-width: 400px is its width. We can even go ahead and add more styles. The video shows what can be achieved from the components:

We have the following styles:

  1. By default (card type).

  2. Horizontal card with small preview.

  3. Horizontal card with large preview.

  4. If the parent is too large, the styling will be similar to the hero section, indicating that this is a featured article.

Let’s dive deeper into examples of using container queries.

Use cases for CSS container queries

Container queries and CSS grid with auto-fit

In some cases, using auto-fit in a CSS grid has unexpected results. For example, a component is too wide and difficult to read. To give some context, here’s a visual showing the difference between auto-fit and auto-fill in a CSS grid:

Note that when using auto-fit, the elements expand to fill the available space. However, in the case of automatic filling, the “ grid elements will not grow, instead we will have free space (the dotted element in the far right corner).

You may be wondering how this fact relates to container requests. Each item in the grid is a container, and when it expands (that is, when we use auto-fit), we need the component to resize based on that expansion.

<div class="o-grid">
  <div class="o-grid__item">
    <article class="c-article"></article>
  </div>
  <div class="o-grid__item">
    <article class="c-article"></article>
  </div>
  <div class="o-grid__item">
    <article class="c-article"></article>
  </div>
  <div class="o-grid__item">
    <article class="c-article"></article>
  </div>
</div>
.o-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  grid-gap: 1rem;
}

When there are four elements, the result should look something like this:

What happens when the number of articles decreases? The fewer elements we have, the wider they will be. This is because we are using auto-fit. The first one looks good, but the last two (2 per row, 1 per row) are not very good: they are too wide:

What if each component of the article changes based on the width of the parent component? If so, auto-fit would be a very good advantage. Here’s what you need to do: if the width of a grid element is greater than 400 pixels, the article should switch to a horizontal style, and you can achieve this like this:

.o-grid__item {
  contain: layout inline-size;
}

@container (min-width: 400px) {
  .c-article {
    display: flex;
    flex-wrap: wrap;
  }
}

Also, if the article is the only item in the grid, you want to display it with a hero section.

.o-grid__item {
  contain: layout inline-size;
}

@container (min-width: 700px) {
  .c-article {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 350px;
  }

  .card__thumb {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
}

That’s all. We have a component that is responsive to the width of the parent component, and it works in any context. Isn’t that awesome? Take a look demo on CodePen.

aside and main

Often we need to configure a component to work in small-width containers such as

Similar Posts

Leave a Reply Cancel reply