Hello, Container Queries

With components being so popular in modern web development, why are we still applying our CSS to the size of the viewport? Container queries are here (…almost). With container queries, you can use CSS to apply styles based on the size of the container, rather than the size of the viewport. This means your components are one big step closer to being a self-contained module that will effectively look and work the same in any project you drop it into. Currently, container queries are working in all the major browsers except for Firefox which can be fixed with a polyfill.

<script src="https://cdn.jsdelivr.net/npm/container-query-polyfill@1/dist/container-query-polyfill.modern.js"></script>

How to Use Container Queries

Here is our example HTML:

<body>
  <div class="wrapper">
    <section class="container">
      <div class="card">
        <h2>Title</h2>
        <div class="block"></div>
      </div>
      <div class="card">
        <h2>Title</h2>
        <div class="block"></div>
      </div>
    </section>

    <aside class="container">
      <div class="card">
        <h2>Title</h2>
        <div class="block"></div>
      </div>
      <div class="card">
        <h2>Title</h2>
        <div class="block"></div>
      </div>
    </aside>
  </div>
</body>

Container queries are fairly simple to setup. The first thing is to declare the container-type on the parent element. Next, you need to specify the conditions for the container query. In this example, I use @container (max-width: 480px) to target containers with the maximum width of 480 pixels. Any element with the class of “card” will have those two styles applied.

.content {
  container-type: inline-size;
}
@container (max-width: 480px) {
  .card {
    display: flex;
    gap: 4px;
  }
}

Final Example

To further illustrate how to utilize container queries, I added more CSS styles. When the “card” element is at least 481 pixels wide, the title is stacked on top of the color block. When the “card” is 480 pixels or less, the container query changes the display to flex which makes the title and the color block inline. I added different colors to the blocks depending if they are in the <section> or <aside> tag.

body {
  font-family: sans-serif;
  padding: 50px;
}
h2 {
  margin: 0;
  padding: 0;
}
.wrapper {
  display: grid;
  grid-template-columns: 70% 30%;
  gap: 20px;
  max-width: 800px;
  margin: 0 10px;
}
.block {
  height: 200px;
  width: 100%;
  background-color: #bada55;
}
aside .block {
  /* blue color block in aside */
  background-color: #8ab5f8;
}
.card + .card {
  margin-top: 20px;
}

/* Normal media query */
@media screen and (max-width: 480px) {
  .wrapper {
    /* Single column layout for mobile view */
    grid-template-columns: 1fr;
  }
}

/* Container declartion */
.container {
  container-type: inline-size;
  max-width: 800px;
}

/* Container query */
@container (max-width: 480px) {
  /* At widths less than 480px, card layout is in a row */
  .card {
    display: flex;
    gap: 4px;
  }
  .card > * {
    height: 24px;
    display: flex;
    align-items: center;
  }
}

You can find a working demo here. Make sure you play with the width of your browser window to see the container query in action.