Lesson 38: Container Queries

With media queries you can change a layout based on the size of the viewport or the type of device. Container queries let you make more specific changes to an element based on the size and state of its ancestors, which are called containers.

Imagine you have a newsletter sign-up form that you want to use in several places on your site. You might want it to span the full width of the page on a sign-up page, but go into a split column on a page with other content.

As shown in this demo, container queries let you change properties like font size, padding, and the layout of the element based on its nearest container. This works no matter the size of the viewport.

Setting up a container query

Unlike media queries, container queries are set up in two parts.

  1. Define a container.
  2. Write styles for a child element to be used when a parent container matches the query’s conditions.

Defining a container

You can define a container by using the container-type property.

.my-container-element {
  container-type: inline-size;
}

A container-type of inline-size lets you query the container’s inline axis.

To query against both the inline and block axes, use container-type: size.

main,
.my-component {
  container-type: size;
}

Both values of container-type apply size containment, but in different ways. The inline-size containment on an element stops its descendants from affecting its inline size.

An element with size containment stops its descendants from affecting its size in both the block and inline axes.

In this example you can see how size containment can affect the element it is applied to.

The container won’t be sized based on the size of its children, like the <p> element. So the container will collapse unless you give it an explicit size. You can set its dimensions with inline-size, block-size, or aspect-ratio, or place it into a layout that already has a size.

Container query conditions

Once a container is set up, you can add a condition, wrapped in parentheses, that must be true for the styles inside the container query to be used. Container size queries are based on the size of ancestor elements. The condition is made up of these parts.

  • a size feature, which is width, height, inline-size, block-size, aspect-ratio, or orientation
  • a comparison operator, such as >, <, =, or >=
  • a length value
.my-container-element {
  container-type: inline-size;
}

@container (inline-size > 30em) {
  .my-child-element {
    /* styles to apply when .my-container-element is wider than 30em */
  }
}

You can also write a size feature condition with a colon and a single value to test.

@container (orientation: landscape) {
  /*...*/
}

@container (min-width: 300px) {
  /*...*/
}

You can combine several conditions with keywords like and and or, or chain conditions together with operators.

@container (inline-size > 40em) and (orientation: landscape)  {
  /*...*/
}

@container (height > 25vh) or (orientation: portrait) {
  /*...*/
}

@container ( 10em <= width <= 500px) {
  /*...*/
}

Naming containers

To target a specific container, even when it is not the nearest ancestor, you can name it with the container-name property. Then you can refer to that container name in your query before you set your conditions.

.sidebar {
  container-name: main-sidebar;
  container-type: inline-size;
}

@container main-sidebar (inline-size > 20em)  {
  .button-group {
    display: flex;
    padding-inline: 1.25em;
  }
}

The named container must still be an ancestor of the elements you are styling.

Use shorthand with the container property

The container property lets you use a shorthand to both define a container and set the container type.

.sidebar {
  container: main-sidebar / inline-size;
}

The name of the container comes before the slash and the type comes after.

Container query units

Inside containers you also get container relative length units. This gives more flexibility for components that can live in different containers. The relative lengths adjust based on the size of the container.

Here the container length unit cqi, which is 1% of a query container’s inline size, is used for the button’s padding.

.container {
  container: button-container / inline-size;
}

.one {
  inline-size: 30vw;
}

.two {
  inline-size: 50vw;
}

button {
  padding: 2cqi 5cqi;
}

Both buttons have the same relative units applied. Since the units are relative to the container’s size, the second button has more padding because its container is larger.

Nesting container queries

You can nest container queries inside selectors.

.my-element {
  display: grid;
  padding: 1em 2em;

  @container my-container (min-inline-size: 22em) {
    /* styles to apply when element's container is wider than 22em */
  }
}

/* equivalent to */
.my-element {
  display: grid;
  padding: 1em 2em;
}

@container my-container (min-inline-size: 22em) {
  .my-element {
     /* styles to apply when element's is wider than 22em */
  }
}

You can also nest them inside other container queries or at-rules.

@container my-container (min-inline-size: 22em) {
  .my-element {
      /* styles to apply when element's is wider than 22em */
  }
}

@layer base {
  @container my-container (min-inline-size: 22em) {
    .my-element {
    /* styles to apply */
    }
  }
}

Adapted from Learn CSS © Google and contributors, licensed under CC BY 4.0 (prose) and Apache 2.0 (code samples).