Container Queries

CSS container queries let a rule depend on the size or the style of an ancestor rather than of the whole screen. How to use them can be learned at MDN Docs.

An element becomes a query container with container-type, and can be given a name with container-name, or both at once with the container shorthand. A @container rule then applies its declarations to elements that have such an ancestor, when the condition holds for it.

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

@container card (min-width: 400px) {
  .card .title {
    font-size: 24px;
  }
}

Container types

ValueDescription
normalThe default. The element is not a size container, but can still answer style queries.
inline-sizeQueries on the width can read this element.
sizeQueries on the width, the height, the aspect ratio and the orientation can read this element.

The size read is the content box, without padding and border. Unlike a browser, ReactUnity does not apply size containment: a container’s own size may still depend on its contents. That is what lets container-type: size be applied freely, and it also means a layout in which a container’s size depends on what its query changes may not settle; such a layout is left where the second pass lands.

A query is answered by the nearest ancestor that is a container of a kind able to answer it. A (height ...) query skips an inline-size container and keeps looking upwards. A query with a name only considers ancestors whose container-name includes it. An element with no eligible ancestor never matches.

Size queries

Features: width, height, inline-size, block-size, aspect-ratio and orientation. Both the min-/max- prefixed form and the range form are accepted, and conditions combine with and, or, not and parentheses.

@container (min-width: 400px) { ... }
@container (width > 400px) { ... }
@container (400px <= width < 800px) { ... }
@container sidebar (orientation: portrait) { ... }
@container (aspect-ratio > 16/9) and (height >= 200px) { ... }
@container not (width < 300px) { ... }

A length in a condition is resolved against the container, so 20em is twenty times the container’s font size.

Style queries

style() compares a property of the container. A custom property compares as the text it was declared with, and style(--theme) alone holds when the property is set to anything. A regular property compares its computed value with the given one.

.panel {
  --theme: dark;
}

@container style(--theme: dark) {
  .panel .title {
    color: white;
  }
}

@container style(--theme) and (width > 300px) { ... }

Every element can answer a style query, whatever its container-type, so an unnamed style query reads the parent. Shorthand properties such as border cannot be queried; query one of the properties they set instead.

Container units

cqw, cqh, cqi, cqb, cqmin and cqmax are a percentage of the nearest size container’s content box on that axis. cqi and cqb mean the same as cqw and cqh. Each axis looks for its own container: an inline-size container answers cqw, while cqh comes from the nearest size container above. Without any container, the units fall back to the viewport, the way vw and vh behave.

.card {
  container-type: inline-size;
}

.card .title {
  font-size: 5cqw;
  padding: 2cqmin;
}

Nesting

A @container rule can be nested in a style rule, in a @media or @supports block, in a @layer, and in another @container. Nested container queries must all hold.

.title {
  font-size: 16px;

  @container card (min-width: 400px) {
    font-size: 24px;
  }
}

Notes

  • In UGUI, a container that changes size is re-queried in the same frame, right after layout. In UIToolkit, where layout belongs to the framework, the change is seen a frame later.
  • A style query compares a custom property’s declared text after collapsing whitespace, so style(--gap: 4px) does not match a value written as calc(2px * 2).