> is omitted,
it's treated as having a condition that's always true.
A conditional rule chain is
a series of consecutive [=conditional group rules=],
starting with a [=conditional group rule=] other than ''@else'',
followed by zero or more ''@else'' rules.
There cannot be anything between the successive [=conditional group rules=]
other than whitespace and/or comments;
any other token “breaks” the chain.
Issue: Should we require that only the last ''@else'' in a chain can have an omitted condition?
It's not uncommon for me, when debugging code,
to short-circuit an if-else chain by setting one of them to "true";
I presume that would be similarly useful in CSS?
It's still pretty easy to see you've done something wrong if you omit the condition accidentally.
Within a [=conditional rule chain=],
the conditions of each [=conditional group rule=] are evaluated in order.
If one of them is true,
the conditions of all following [=conditional group rules=] in the chain
evaluate to false,
regardless of their stated condition.
An ''@else'' rule that is not part of a [=conditional rule chain=]
is invalid and must be ignored.
For example, here's a (somewhat silly) conditional chain:
@when media(width >= 400px) and media(pointer: fine) and supports(display: flex) {
/* A */
} @else supports(caret-color: pink) and supports(background: double-rainbow()) {
/* B */
} @else {
/* C */
}
Exactly one of the preceding rules will be chosen,
even though the second rule
doesn't exclude large widths, fine points, or flexbox support,
and the last rule
doesn't specify anything at all.
To achieve the same result without [=conditional rule chains=],
you'd need to write:
@media (width >= 400px) and (pointer: fine) {
@supports (display: flex) {
/* A */
}
@supports not (display: flex) {
@supports (caret-color: pink) and (background: double-rainbow()) {
/* B */
}
@supports not ((caret-color: pink) and (background: double-rainbow())) {
/* C */
}
}
}
@media not ((width >= 400px) and (pointer: fine)) {
@supports (caret-color: pink) and (background: double-rainbow()) {
/* B */
}
@supports not ((caret-color: pink) and (background: double-rainbow())) {
/* C */
}
}
This is simultaneously hard to read,
requires significant duplication of both conditions and contents,
and is
very difficult to write correctly.
If the conditions got any more complicated
(which is not unusual in real-world content),
the example would get
significantly worse.
In this example, three different color font technologies
are tested, in order of preference,
plus a monochrome fallback.
The most capable, COLRv1, supports both gradients and font variations;
the next best choice, SVG, supports gradients
while the least capable, COLRv0, supports flat color fill only.
The fallback has no test condition,
so will always be chosen unless one of the earlier conditions succeeds.
@when font-tech(color-COLRv1) and font-tech(variations) {
@font-face { font-family: icons; src: url(icons-gradient-var.woff2); }
}
@else font-tech(color-SVG) {
@font-face { font-family: icons; src: url(icons-gradient.woff2); }
}
@else font-tech(color-COLRv0) {
@font-face { font-family: icons; src: url(icons-flat.woff2); }
}
@else {
@font-face { font-family: icons; src: url(icons-fallback.woff2); }
}
Notice that in this example,
the variable color font is only downloaded
if COLRv1 is supported
and font variations are also supported.
Notice too that only one of the available options will be downloaded;
this would not be the case without ''@when'' and ''@else'',
as the next example shows.
In this example,
although it appears that the fallback will not be used
if COLRv1 is supported,
in fact both fonts will be downloaded,
which wastes bandwidth if it is not used.
The fallback might still be used for some characters;
for example, if the color font supports only Latin,
while the fallback supports Latin and Greek.
@font-face { font-family: icons; src: url(icons-fallback.woff2);
@supports font-tech(color-COLRv1) {
@font-face { font-family: icons; src: url(icons-gradient-var.woff2); }
}
Container Queries
While [=media queries=] provide a method to query
aspects of the user agent or device environment
that a document is being displayed in
(such as viewport dimensions or user preferences),
[=container queries=] allow testing aspects of elements within the document
(such as box dimensions or computed styles).
By default, all elements are query containers
for the purpose of [=container style queries=],
and can be established as [=query containers=]
for [=container size queries=] and [=container scroll-state queries=] by specifying
the additional query types using the 'container-type' property
(or the 'container' [=shorthand=]).
Style rules applying to a [=query container=]’s [=flat tree=] descendants
can be conditioned by querying against it,
using the ''@container'' [=conditional group rule=].
For example, we can define the main content area and sidebar as containers,
and then describe a ''.media-object'' that changes
from vertical to horizontal layout depending on the size of its container:
main, aside {
container: my-layout / inline-size;
}
.media-object {
display: grid;
grid-template: 'img' auto 'content' auto / 100%;
}
@container my-layout (inline-size > 45em) {
.media-object {
grid-template: 'img content' auto / auto 1fr;
}
}
Media objects in the main and sidebar areas
will each respond to their own container context.
For the ''::part()'' and ''::slotted()'' pseudo-element selectors,
which represent real elements in the DOM tree, [=query containers=] can be
established by [=flat tree=] ancestors of those elements.
For other pseudo-elements, [=query containers=] can be established by
inclusive [=flat tree=] ancestors of their originating element.
It follows that:
* ''::before'', ''::after'', ''::marker'', and ''::backdrop'' query their
originating elements
* ''::first-letter'' and ''::first-line'' query their originating elements,
even if the
fictional tag sequence may push the
::first-line
past other elements for the purpose of
inheritance and rendering
* ''::slotted()'' selectors can query containers inside the shadow tree,
including the slot itself
* ''::slotted()::before'' selectors can query the slotted shadow host child
* ''::part()'' selectors can query containers inside the shadow tree
* ''::placeholder'' and ''::file-selector-button'' can query the input
element, but do not expose any internal containers if the input element is
implemented using a shadow tree
A ::before selector querying the size of the originating element:
<style>
#container {
width: 100px;
container-type: inline-size;
}
@container (inline-size < 150px) {
#inner::before {
content: "BEFORE";
}
}
</style>
<div id=container>
<span id=inner></span>
</div>
A ::slotted() selector for styling a shadow host child can query a
container in the shadow tree:
<div id=host style="width:200px">
<template shadowroot=open>
<style>
#container {
width: 100px;
container-type: inline-size;
}
@container (inline-size < 150px) {
::slotted(span) {
color: green;
}
}
</style>
<div id=container>
<slot />
</div>
</template>
<span id=slotted>Green</span>
</div>
Creating Query Containers: the 'container-type' property
Name: container-type
Value: normal | [ [ size | inline-size ] || scroll-state ]
Initial: normal
Inherited: no
Applies to: all elements
Computed value: specified keyword
Animation type: not animatable
The 'container-type' property establishes the element as a
[=query container=] for certain types of queries. For size
[=container queries=], which require certain types of containment, elements
are explicitly made [=query containers=] through this property. For other
types of [=query containers=] any element can be a [=query container=], such
as for [=container style queries=].
Values have the following meanings:
- size
-
Establishes a [=query container=] for [=container size queries=]
on both the [=inline axis|inline=] and [=block axis=].
Applies [=style containment=] and [=size containment=]
to the [=principal box=],
and establishes an [=independent formatting context=].
- inline-size
-
Establishes a [=query container=] for [=container size queries=]
on the container’s own [=inline axis=].
Applies [=style containment=] and [=inline-size containment=]
to the [=principal box=],
and establishes an [=independent formatting context=].
- scroll-state
-
Establishes a [=query container=] for [=scroll-state queries=]
- normal
-
The element is not a [=query container=]
for any [=container size queries=] or [=scroll-state queries=],
but remains a [=query container=] for [=container style queries=].
For example, authors can create container-responsive typography,
adjusting 'font-size', 'line-height', and other typographic concerns
based on the size of a container:
aside, main {
container-type: inline-size;
}
h2 { font-size: 1.2em; }
@container (width > 40em) {
h2 { font-size: 1.5em; }
}
The ''40em'' value used in the query condition
is relative to the [=computed value=] of 'font-size'
on the relevant [=query container=].
Containers can also expose computed style values for querying.
This can be useful for toggling behavior across multiple properties:
@container style(--cards: small) {
article {
border: thin solid silver;
border-radius: 0.5em;
padding: 1em;
}
}
Containers can also expose state that depends on scroll offset. This example
styles a descendant of a sticky positioned element when it is stuck to the
top edge:
#sticky {
container-type: scroll-state;
position: sticky;
}
@container scroll-state(stuck: top) {
#sticky-child {
background-color: lime;
}
}
Naming Query Containers: the 'container-name' property
Name: container-name
Value: none | <>+
Initial: none
Inherited: no
Applies to: all elements
Computed Value: the keyword ''container-name/none'', or an ordered list of [=identifiers=]
Animation type: not animatable
The 'container-name' property
specifies a list of query container names.
These names can be used by ''@container'' rules
to filter which [=query containers=] are targeted.
- none
-
The [=query container=] has no [=query container name=].
- <>
-
Specifies a [=query container name=] as an [=identifier=].
The keywords ''container-name/none'', ''and'', ''not'', and ''or'' are excluded from this <>.
In some cases, we want to query aspects of a specific container,
even if it’s not the nearest ancestor container.
For example, we might want to query the height of a main content area,
and the width of a more nested inline-container.
main {
container-type: size;
container-name: my-page-layout;
}
.my-component {
container-type: inline-size;
container-name: my-component-library;
}
@container my-page-layout (block-size > 12em) {
.card { margin-block: 2em; }
}
@container my-component-library (inline-size > 30em) {
.card { margin-inline: 2em; }
}
It is also possible to query for a container only based on its name.
@container my-page-layout {
.card { padding: 1em; }
}
Creating Named Containers: the 'container' shorthand
Name: container
Value: <<'container-name'>> [ / <<'container-type'>> ]?
container-queries/animation-container-size.html
container-queries/animation-container-type-dynamic.html
container-queries/animation-nested-animation.html
container-queries/animation-nested-transition.html
container-queries/aspect-ratio-feature-evaluation.html
container-queries/at-container-parsing.html
container-queries/at-container-serialization.html
container-queries/at-container-style-parsing.html
container-queries/at-container-style-serialization.html
container-queries/auto-scrollbars.html
container-queries/backdrop-invalidation.html
container-queries/calc-evaluation.html
container-queries/canvas-as-container-001.html
container-queries/canvas-as-container-002.html
container-queries/canvas-as-container-003.html
container-queries/canvas-as-container-004.html
container-queries/canvas-as-container-005.html
container-queries/canvas-as-container-006.html
container-queries/change-display-in-container.html
container-queries/chrome-legacy-skip-recalc.html
container-queries/column-spanner-in-container.html
container-queries/conditional-container-status.html
container-queries/container-computed.html
container-queries/container-for-cue.html
container-queries/container-for-shadow-dom.html
container-queries/container-inheritance.html
container-queries/container-inner-at-rules.html
container-queries/container-inside-multicol-with-table.html
container-queries/container-longhand-animation-type.html
container-queries/container-name-computed.html
container-queries/container-name-invalidation.html
container-queries/container-name-parsing.html
container-queries/container-name-tree-scoped.html
container-queries/container-nested.html
container-queries/container-parsing.html
container-queries/container-selection-unknown-features.html
container-queries/container-selection.html
container-queries/container-size-invalidation-after-load.html
container-queries/container-size-invalidation.html
container-queries/container-size-nested-invalidation.html
container-queries/container-size-shadow-invalidation.html
container-queries/container-type-computed.html
container-queries/container-type-containment.html
container-queries/container-type-invalidation.html
container-queries/container-type-layout-invalidation.html
container-queries/container-type-parsing.html
container-queries/container-units-animation.html
container-queries/container-units-basic.html
container-queries/container-units-computational-independence.html
container-queries/container-units-content-box.html
container-queries/container-units-gradient-invalidation.html
container-queries/container-units-gradient.html
container-queries/container-units-in-at-container-dynamic.html
container-queries/container-units-in-at-container-fallback.html
container-queries/container-units-in-at-container.html
container-queries/container-units-ineligible-container.html
container-queries/container-units-invalidation.html
container-queries/container-units-media-queries.html
container-queries/container-units-rule-cache.html
container-queries/container-units-selection.html
container-queries/container-units-shadow.html
container-queries/container-units-sharing-via-rule-node.html
container-queries/container-units-small-viewport-fallback.html
container-queries/container-units-svglength.html
container-queries/container-units-typed-om.html
container-queries/counters-flex-circular.html
container-queries/counters-in-container-dynamic.html
container-queries/counters-in-container.html
container-queries/crashtests/br-crash.html
container-queries/crashtests/canvas-as-container-crash.html
container-queries/crashtests/chrome-bug-1289718-000-crash.html
container-queries/crashtests/chrome-bug-1289718-001-crash.html
container-queries/crashtests/chrome-bug-1346969-crash.html
container-queries/crashtests/chrome-bug-1362391-crash.html
container-queries/crashtests/chrome-bug-1429955-crash.html
container-queries/crashtests/chrome-bug-1505250-crash.html
container-queries/crashtests/chrome-bug-346264227-crash.html
container-queries/crashtests/chrome-bug-372358471-crash.html
container-queries/crashtests/chrome-custom-highlight-crash.html
container-queries/crashtests/chrome-layout-root-crash.html
container-queries/crashtests/chrome-quotes-crash.html
container-queries/crashtests/chrome-remove-insert-evaluator-crash.html
container-queries/crashtests/columns-in-table-001-crash.html
container-queries/crashtests/columns-in-table-002-crash.html
container-queries/crashtests/container-in-canvas-crash.html
container-queries/crashtests/container-type-change-chrome-legacy-crash.html
container-queries/crashtests/dialog-backdrop-crash.html
container-queries/crashtests/dirty-rowgroup-crash.html
container-queries/crashtests/flex-in-columns-000-crash.html
container-queries/crashtests/flex-in-columns-001-crash.html
container-queries/crashtests/flex-in-columns-002-crash.html
container-queries/crashtests/flex-in-columns-003-crash.html
container-queries/crashtests/focus-inside-content-visibility-crash.html
container-queries/crashtests/force-sibling-style-crash.html
container-queries/crashtests/grid-in-columns-000-crash.html
container-queries/crashtests/grid-in-columns-001-crash.html
container-queries/crashtests/grid-in-columns-002-crash.html
container-queries/crashtests/grid-in-columns-003-crash.html
container-queries/crashtests/iframe-init-crash.html
container-queries/crashtests/inline-multicol-inside-container-crash.html
container-queries/crashtests/inline-with-columns-000-crash.html
container-queries/crashtests/inline-with-columns-001-crash.html
container-queries/crashtests/input-column-group-container-crash.html
container-queries/crashtests/input-placeholder-inline-size-crash.html
container-queries/crashtests/marker-gcs-after-disconnect-crash.html
container-queries/crashtests/math-block-container-child-crash.html
container-queries/crashtests/mathml-container-type-crash.html
container-queries/crashtests/orthogonal-replaced-crash.html
container-queries/crashtests/pseudo-container-crash.html
container-queries/crashtests/remove-dom-child-change-style.html
container-queries/crashtests/reversed-ol-crash.html
container-queries/crashtests/size-change-during-transition-crash.html
container-queries/crashtests/svg-layout-root-crash.html
container-queries/crashtests/svg-resource-in-container-crash.html
container-queries/crashtests/svg-text-crash.html
container-queries/crashtests/table-in-columns-000-crash.html
container-queries/crashtests/table-in-columns-001-crash.html
container-queries/crashtests/table-in-columns-002-crash.html
container-queries/crashtests/table-in-columns-003-crash.html
container-queries/crashtests/table-in-columns-004-crash.html
container-queries/crashtests/table-in-columns-005-crash.html
container-queries/crashtests/top-layer-crash.html
container-queries/crashtests/top-layer-nested-crash.html
container-queries/custom-layout-container-001.https.html
container-queries/custom-property-style-queries.html
container-queries/custom-property-style-query-change.html
container-queries/deep-nested-inline-size-containers.html
container-queries/dialog-backdrop-create.html
container-queries/dialog-backdrop-remove.html
container-queries/display-contents-dynamic-style-queries.html
container-queries/display-contents.html
container-queries/display-in-container.html
container-queries/display-none.html
container-queries/fieldset-legend-change.html
container-queries/flex-basis-with-container-type.html
container-queries/font-relative-calc-dynamic.html
container-queries/font-relative-units-dynamic.html
container-queries/font-relative-units.html
container-queries/fragmented-container-001.html
container-queries/get-animations.html
container-queries/grid-container.html
container-queries/grid-item-container.html
container-queries/idlharness.html
container-queries/iframe-in-container-invalidation.html
container-queries/iframe-invalidation.html
container-queries/ineligible-containment.html
container-queries/inheritance-from-container.html
container-queries/inline-size-and-min-width.html
container-queries/inline-size-bfc-floats.html
container-queries/inline-size-containment-vertical-rl.html
container-queries/inline-size-containment.html
container-queries/inner-first-line-non-matching.html
container-queries/layout-dependent-focus.html
container-queries/multicol-container-001.html
container-queries/multicol-inside-container.html
container-queries/nested-query-containers.html
container-queries/nested-size-style-container-invalidation.html
container-queries/never-match-container.html
container-queries/no-layout-containment-abspos-dynamic.html
container-queries/no-layout-containment-abspos.html
container-queries/no-layout-containment-baseline.html
container-queries/no-layout-containment-fixedpos-dynamic.html
container-queries/no-layout-containment-fixedpos.html
container-queries/no-layout-containment-scroll.html
container-queries/no-layout-containment-subgrid-crash.html
container-queries/orthogonal-wm-container-query.html
container-queries/percentage-padding-orthogonal.html
container-queries/pseudo-elements-001.html
container-queries/pseudo-elements-002.html
container-queries/pseudo-elements-002b.html
container-queries/pseudo-elements-003.html
container-queries/pseudo-elements-004.html
container-queries/pseudo-elements-005.html
container-queries/pseudo-elements-006.html
container-queries/pseudo-elements-007.html
container-queries/pseudo-elements-008.html
container-queries/pseudo-elements-009.html
container-queries/pseudo-elements-010.html
container-queries/pseudo-elements-011.html
container-queries/pseudo-elements-012.html
container-queries/pseudo-elements-013.html
container-queries/query-content-box.html
container-queries/query-evaluation-style.html
container-queries/query-evaluation.html
container-queries/reattach-container-with-dirty-child.html
container-queries/registered-color-style-queries.html
container-queries/resize-while-content-visibility-hidden.html
container-queries/scroll-state/at-container-scrollable-parsing.html
container-queries/scroll-state/at-container-scrollable-serialization.html
container-queries/scroll-state/at-container-snapped-parsing.html
container-queries/scroll-state/at-container-snapped-serialization.html
container-queries/scroll-state/at-container-stuck-parsing.html
container-queries/scroll-state/at-container-stuck-serialization.html
container-queries/scroll-state/container-type-scroll-state-computed.html
container-queries/scroll-state/container-type-scroll-state-containment.html
container-queries/scroll-state/container-type-scroll-state-parsing.html
container-queries/scroll-state/scroll-state-initially-scrollable.html
container-queries/scroll-state/scroll-state-initially-snapped.html
container-queries/scroll-state/scroll-state-initially-stuck.html
container-queries/scroll-state/scroll-state-scrollable-change.html
container-queries/scroll-state/scroll-state-scrollable-container-type-change.html
container-queries/scroll-state/scroll-state-scrollable-layout-change.html
container-queries/scroll-state/scroll-state-scrollable-wm.html
container-queries/scroll-state/scroll-state-snapped-change.html
container-queries/scroll-state/scroll-state-snapped-container-type-change.html
container-queries/scroll-state/scroll-state-snapped-layout-change.html
container-queries/scroll-state/scroll-state-snapped-none.html
container-queries/scroll-state/scroll-state-snapped-snap-changing.html
container-queries/scroll-state/scroll-state-snapped-wm.html
container-queries/scroll-state/scroll-state-stuck-container-type-change.html
container-queries/scroll-state/scroll-state-stuck-layout-change.html
container-queries/scroll-state/scroll-state-stuck-writing-direction.html
container-queries/scroll-state/scroll-state-target-query-change.html
container-queries/scrollbar-container-units-block.html
container-queries/scrollbar-container-units-inline.html
container-queries/sibling-layout-dependency.html
container-queries/size-container-no-principal-box.html
container-queries/size-container-with-quotes.html
container-queries/size-container-writing-mode-change.html
container-queries/size-feature-evaluation.html
container-queries/style-change-in-container.html
container-queries/style-container-for-shadow-dom.html
container-queries/style-container-invalidation-inheritance.html
container-queries/style-not-sharing-float.html
container-queries/style-query-document-element.html
container-queries/style-query-no-cycle.html
container-queries/style-query-with-unknown-width.html
container-queries/svg-foreignobject-child-container.html
container-queries/svg-foreignobject-no-size-container.html
container-queries/svg-g-no-size-container.html
container-queries/svg-root-size-container.html
container-queries/table-inside-container-changing-display.html
container-queries/top-layer-dialog-backdrop.html
container-queries/top-layer-dialog-container.html
container-queries/top-layer-dialog.html
container-queries/top-layer-nested-dialog.html
container-queries/transition-scrollbars.html
container-queries/transition-style-change-event-002.html
container-queries/transition-style-change-event.html
container-queries/unsupported-axis.html
container-queries/viewport-units-dynamic.html
container-queries/viewport-units.html
container-queries/whitespace-update-after-removal.html
The 'container' [=shorthand property=] sets
both 'container-type' and 'container-name' in the same declaration.
If <<'container-type'>> is omitted,
it is reset to its [=initial value=].
We can define both a 'container-type' and 'container-name'
using the shorthand syntax:
main {
container: my-layout / size;
}
.grid-item {
container: my-component / inline-size;
}
Container Queries: the ''@container'' rule
The @container rule
is a [=conditional group rule=] whose condition contains
a container query,
which is a boolean combination of [=container size queries=] and/or [=container style queries=].
Style declarations within the <> block of an ''@container'' rule
are [[css-cascade-4#filtering|filtered]] by its condition
to only match when the [=container query=]
is true for their element’s [=query container=].
The syntax of the ''@container'' rule is:
@container <># {
<>
}
where:
<> = [ <>? <>? ]!
<> = <>
<> = not <>
| <> [ [ and <> ]* | [ or <> ]* ]
<> = ( <> )
| ( <> )
| style( <> )
| scroll-state( <> )
| <>
<> = not <>
| <> [ [ and <> ]* | [ or <> ]* ]
| <>
<> = ( <> )
| ( <> )
| <>
<> = not <>
| <> [ [ and <> ]* | [ or <> ]* ]
| <>
<> = ( <> )
| ( <> )
| <>
The keywords ''container-name/none'', ''and'', ''not'', and ''or''
are excluded from the <> above.
For each element,
the [=query container=] to be queried
is selected from among the element’s ancestor [=query containers=]
that are established as a valid [=query container=]
for all the [=container features=]
in the <>. If the <> contains
unknown or unsupported [=container feature=]s,
no [=query container=] will be selected for that <>.
The <> filters the set of [=query containers=] considered
to just those with a matching [=query container name=].
Once an eligible [=query container=] has been selected for an element,
each [=container feature=] in the <>
is evaluated against that [=query container=].
If no ancestor is an eligible [=query container=],
then the [=container query=] is ''unknown'' for that element.
As with media queries, <> evaluates to ''unknown''.
If the <> is omitted, the [=query container=] is eligible as
long as the <> matches.
If a [=container query=] includes multiple <>s,
each condition will select it's own [=query container=],
and evaluate independently.
A [=container query=] is ''true'' if any of its component
<>s are ''true'',
and ''false'' only if all of its component
<>s are ''false''.
As with [=media queries=],
we can string together multiple queries in a single condition:
@container card (inline-size > 30em) and style(--responsive: true) {
/* styles */
}
The styles above will only be applied
if there is an ancestor container named "card"
that meets both the '@container/inline-size'
and [=container style query|style=] conditions.
We can also combine multiple conditions into a list,
with each condition evaluating against a different container:
@container card (inline-size > 30em), style(--large: true) {
/* styles */
}
The styles above will be applied
if there is an ancestor container named "card"
that meets the '@container/inline-size' condition
or the nearest style container
meets the [=container style query|style=] condition.
Style rules defined on an element inside multiple nested [=container queries=]
apply when all of the wrapping [=container queries=] are true for that element.
Note: Nested [=container queries=] can evaluate in relation to different containers,
so it is not always possible to merge the individual <>s
into a single query.
Using a single comma-separated [=container query=],
we can query multiple containers:
@container card (inline-size > 30em), style(--responsive: true) {
/* styles */
}
The styles above will apply for an element inside
either
a container named "card" that meets the '@container/inline-size' condition,
or a container meeting the [=container style query|style=] condition.
In order to require that
all conditions are met
while querying multiple containers,
we would need to nest multiple queries:
@container card (inline-size > 30em) {
@container style(--responsive: true) {
/* styles */
}
}
The styles above will only be applied
if there is
both an ancestor container named "card"
that meets the '@container/inline-size' condition,
and an ancestor container
meeting the [=container style query|style=] condition.
Global, name-defining [=at-rules=]
such as ''@keyframes'' or ''@font-face'' or ''@layer''
that are defined inside [=container queries=]
are not constrained by the [=container query=] conditions.
Animated Containers
A change in the evaluation of a [=container query=] must be part of a [=style change event=],
even when the change occurred because of [=effect values|animation effects=].
A transition on a sibling element can indirectly affect the size of a
container, triggering [=style change events=] whenever container queries
change their evaluation as a result:
main {
display: flex;
width: 300px;
}
#container {
container-type: inline-size;
flex: 1;
}
/* Resolved width is initially 200px, but changes as the transition
on #sibling progresses. */
#inner {
transition: 1s background-color;
background-color: tomato;
}
/* When this container query starts (or stops) applying, a transition
must start on background-color on #inner. */
@container (width <= 150px) {
#inner {
background-color: skyblue;
}
}
#sibling {
width: 100px;
transition: width 1s;
}
#sibling:hover {
width: 200px;
}
<main>
<div id=container>
<div id=inner>Inner</div>
</div>
<div id=sibling>Sibling</div>
</main>
Changes in [=computed values=] caused by [=container query length=] units
must also be part of a [=style change event=].
Container Features
A container feature
queries a specific aspect of a [=query container=].
[=Container features=] use the same rules as [=media features=] when evaluating
in a [=boolean context=].
Size Container Features
A container size query
allows querying
the size of the [=query container=]’s [=principal box=].
It is a boolean combination of
individual size features (<>)
that each query a single, specific dimensional feature of the [=query container=].
The syntax of a <> is the same as for a [=media feature=]:
a feature name, a comparator, and a value.
[[mediaqueries-5]]
The boolean syntax and logic combining [=size features=] into a [=container size query|size query=]
is the same as for [=CSS feature queries=].
(See ''@supports''. [[!css-conditional-3]])
If the [=query container=] does not have a [=principal box=],
or the principal box is not a [=layout containment box=],
or the [=query container=] does not support [=container size queries=] on the relevant axes,
then the result of evaluating the [=size feature=] is unknown.
[=Relative length=] units
(including [=container query length=] units)
and [=custom properties=]
in [=container query=] conditions
are evaluated based on the [=computed values=] of the [=query container=].
Note: This is different from the handling of relative units in [=media queries=].
Note: If [=custom property=] substitution results in an invalid value for the
[=size feature=], it is handled the same as other invalid feature values,
and the result of the [=size feature=] is ''unknown''.
For example, [=query containers=] with different font-sizes
will evaluate ''em''-based queries relative to their own font sizes:
aside, main {
container-type: inline-size;
}
aside { font-size: 16px; }
main { font-size: 24px; }
@container (width > 40em) {
h2 { font-size: 1.5em; }
}
The ''40em'' value used in the query condition
is relative to the [=computed value=] of 'font-size'
on the relevant [=query container=]:
* For any ''h2'' inside ''aside'',
the query condition will be true above ''640px''.
* For any ''h2'' inside ''main'',
the query condition will be true above ''960px''.
Similarly, [=query containers=] will evaluate ''var()''-based queries
relative to their own [=computed value=] of the [=custom property=]:
aside, main {
container-type: inline-size;
}
aside { --query: 300px; }
main { --query: 500px; }
@container (width > var(--query)) {
h2 { font-size: 1.5em; }
}
The ''var(--query)'' value used in the query condition
is substituted with the [=computed value=] of
the ''--query'' [=custom property=] on the relevant [=query container=]:
* For any ''h2'' inside ''aside'',
the query condition will be true above ''300px''.
* For any ''h2'' inside ''main'',
the query condition will be true above ''500px''.
Width: the '@container/width' feature
Name: width
For: @container
Value: <>
Type: range
The '@container/width' [=container feature=]
queries the [=width=]
of the [=query container=]’s [=content box=].
Height: the '@container/height' feature
Name: height
For: @container
Value: <>
Type: range
The '@container/height' [=container feature=]
queries the [=height=]
of the [=query container=]’s [=content box=].
Inline-size: the '@container/inline-size' feature
Name: inline-size
For: @container
Value: <>
Type: range
The '@container/inline-size' [=container feature=]
queries the [=size=]
of the [=query container=]’s [=content box=]
in the [=query container=]’s [=inline axis=].
Block-size: the '@container/block-size' feature
Name: block-size
For: @container
Value: <>
Type: range
The '@container/block-size' [=container feature=]
queries the [=size=]
of the [=query container=]’s [=content box=]
in the [=query container=]’s [=block axis=].
Aspect-ratio: the '@container/aspect-ratio' feature
Name: aspect-ratio
For: @container
Value: <>
Type: range
The '@container/aspect-ratio' [=container feature=] is defined as the ratio
of the value of the '@container/width' [=container feature=]
to the value of the '@container/height' [=container feature=].
Orientation: the '@container/orientation' feature
Name: orientation
For: @container
Value: portrait | landscape
Type: discrete
- portrait
-
The '@container/orientation' [=container feature=] is ''portrait''
when the value of the '@container/height' [=container feature=]
is greater than or equal to
the value of the '@container/width' [=container feature=].
- landscape
- Otherwise '@container/orientation' is ''landscape''.
Style Container Features
A container style query
allows querying
the [=computed values=] of the [=query container=].
It is a boolean combination of
individual style features (<>)
that each query a single, specific property of the [=query container=].
The syntax of a <> is either the same as for a valid [=declaration=]
[[!CSS-SYNTAX-3]], a [=supported CSS property=], or a <>.
Its query evaluates to true if the [=computed value=] of the given property on the
[=query container=] matches the given value (which is also [=computed value|computed=]
with respect to the [=query container=]), and false otherwise.
A [=style feature=] without a value evaluates to true if the [=computed value=]
is different from the [=initial value=] for the given [=property=].
The boolean syntax and logic combining [=style features=] into a [=container style query|style query=]
is the same as for [=CSS feature queries=].
(See ''@supports''. [[!css-conditional-3]])
[=Style features=] that query a [=shorthand property=] are true if the
[=computed values=] match for each of its [=longhand properties=],
and false otherwise.
[=Cascade-dependent keywords=], such as ''revert'' and ''revert-layer'',
are invalid as values in a [=style feature=], and cause the
[=container style query=] to be false.
Note: The remaining non-cascade-dependent [=CSS-wide keywords=]
are [=computed value|computed=] with respect to the [=query container=],
the same as other values.
A container scroll-state query allows querying a container for
state that depends on scroll position. It is a boolean combination of
individual scroll-state features
(<>) that each query a single feature of the
[=query container=]. The syntax of a <> is the
same as for a [=media feature=]: a feature name, a comparator, and a value.
[=Scroll-state features=] can either match state of the scroller itself,
or an element that is affected by the scroll position of an ancestor
[=scroll container's=] [=scrollport=]. An example of the former is the
''scrollable'' feature, ''snapped'' the latter.
Scroll state may cause layout cycles since queried scroll state may cause style changes,
which may lead to scroll state changes as a result of layout.
To avoid such layout cycles, ''scroll-state'' [=query containers=] update their
current state as part of [=run snapshot post-layout state steps=] which is only
run at specific points in the
HTML event loop processing model.
When asked to [=run snapshot post-layout state steps=], update the current state
of every ''scroll-state'' [=query container=]. This snapshotted state will be used
for any style and layout updates until the next time these steps are run.
Sticky positioning: the '@container/stuck' feature
Name: stuck
For: @container
Value: none | top | right | bottom | left | block-start | inline-start | block-end | inline-end
Type: discrete
The '@container/stuck' [=container feature=] queries whether a
''position/sticky'' positioned container is visually shifted to stay inside
the [=sticky view rectangle=] for the given edge. The logical edges map to
physical based on the direction and writing-mode of the [=query container=].
None of the values match if the [=query container=] is not [=sticky positioned=].
It is possible for two values from opposite axes to match at the same time,
but not for opposite edges along the same axis.
May match:
@container scroll-state((stuck: top) and (stuck: left)) { ... }
Will never match:
@container scroll-state((stuck: left) and (stuck: right)) { ... }
- none
-
The ''position/sticky'' container is not shifted in any direction.
- top
-
The ''position/sticky'' container is shifted to stay inside the top edge.
- right
-
The ''position/sticky'' container is shifted to stay inside the right edge.
- bottom
-
The ''position/sticky'' container is shifted to stay inside the bottom edge.
- left
-
The ''position/sticky'' container is shifted to stay inside the left edge.
- block-start
-
The ''position/sticky'' container is shifted to stay inside the [=block-start=] edge.
- inline-start
-
The ''position/sticky'' container is shifted to stay inside the [=inline-start=] edge.
- block-end
-
The ''position/sticky'' container is shifted to stay inside the [=block-end=] edge.
- inline-end
-
The ''position/sticky'' container is shifted to stay inside the [=inline-end=] edge.
Scroll snapping: the '@container/snapped' feature
Name: snapped
For: @container
Value: none | x | y | block | inline | both
Type: discrete
The '@container/snapped' [=container feature=] queries whether a [=snap target=]
is, or would be, snapped to its [=snap container=], in the given axis. That is,
it matches any [=snap target=] that the {{scrollsnapchanging}} event is fired for.
- none
-
The [=query container=] is not a [=snap target=].
- x
-
'@container/snapped' [=container feature=] matches ''x''
if the [=query container=] is a horizontal [=snap target=] for its [=scroll container=].
- y
-
'@container/snapped' [=container feature=] matches ''y''
if the [=query container=] is a vertical [=snap target=] for its [=scroll container=].
- block
-
'@container/snapped' [=container feature=] matches ''block''
if the [=query container=] is a [=snap target=] for its [=scroll container=].
in the block direction of the [=snap container=].
- inline
-
'@container/snapped' [=container feature=] matches ''inline''
if the [=query container=] is a [=snap target=] for its [=scroll container=]
in the inline direction of the [=snap container=].
- both
-
'@container/snapped' [=container feature=] matches ''both''
if the [=query container=] is a [=snap target=] for its [=scroll container=]
in both directions of the [=snap container=].
Name: scrollable
For: @container
Value: none | top | right | bottom | left | block-start | inline-start | block-end | inline-end | x | y | block | inline
Type: discrete
The '@container/scrollable' [=container feature=] queries whether a
[=scroll container=] has clipped [=scrollable overflow rectangle=] content
in the given direction which is reachable through user initiated scrolling.
That is, '@container/scrollable' does not match for a ''overflow/hidden''
container, nor for a [=negative scrollable overflow region=].
The logical values map to physical based on the direction and writing-mode of
the [=query container=]. None of the values match if the container is not a
[=scroll container=].
- none
-
The [=scroll container=] does not have [=scrollable overflow=] in any direction.
- top
-
The [=scroll container=] has [=scrollable overflow=] past the top edge.
- right
-
The [=scroll container=] has [=scrollable overflow=] past the right edge.
- bottom
-
The [=scroll container=] has [=scrollable overflow=] past the bottom edge.
- left
-
The [=scroll container=] has [=scrollable overflow=] past the left edge.
- block-start
-
The [=scroll container=] has [=scrollable overflow=] past the [=block-start=] edge.
- inline-start
-
The [=scroll container=] has [=scrollable overflow=] past the [=inline-start=] edge.
- block-end
-
The [=scroll container=] has [=scrollable overflow=] past the [=block-end=] edge.
- inline-end
-
The [=scroll container=] has [=scrollable overflow=] past the [=inline-end=] edge.
- x
-
The [=scroll container=] has horizontally [=scrollable overflow=].
- y
-
The [=scroll container=] has vertically [=scrollable overflow=].
- block
-
The [=scroll container=] has [=scrollable overflow=] in its block direction.
- inline
-
The [=scroll container=] has [=scrollable overflow=] in its inline direction.
Container Relative Lengths: the ''cqw'', ''cqh'', ''cqi'', ''cqb'', ''cqmin'', ''cqmax'' units
Container query length units
specify a length relative to the dimensions of a [=query container=].
Style sheets that use [=container query length=] units can more easily move components
from one [=query container=] to another.
The [=container query length=] units are:
Informative Summary of Container Units
unit | relative to
|
''cqw''
| 1% of a [=query container=]’s [=width=]
|
''cqh''
| 1% of a [=query container=]’s [=height=]
|
''cqi''
| 1% of a [=query container=]’s [=inline size=]
|
''cqb''
| 1% of a [=query container=]’s [=block size=]
|
''cqmin''
| The smaller value of ''cqi'' or ''cqb''
|
''cqmax''
| The larger value of ''cqi'' or ''cqb''
|
For each element,
[=container query length=] units are evaluated
as [=container size queries=] on the relevant axis (or axes)
described by the unit.
The [=query container=] for each axis
is the nearest ancestor container
that accepts [=container size queries=] on that axis.
If no eligible [=query container=] is available,
then use the [=small viewport size=] for that axis.
Note: In some cases ''cqi'' and ''cqb'' units on the same element
will evaluate in relation to different [=query containers=].
Similarly, ''cqmin'' and ''cqmax'' units represent
the larger or smaller of the ''cqi'' and ''cqb'' units,
even when those dimensions come from different [=query containers=].
Child elements do not inherit the relative values as specified for their parent;
they inherit the computed values.
Authors can ensure that [=container query length=] units
have an appropriate [=query container=]
by applying them inside a [=container query=]
that relies on the same ''container-type''.
Custom fallback values can be defined outside the [=container query=]:
/* The fallback value does not rely on containment */
h2 { font-size: 1.2em; }
@container (inline-size >= 0px) {
/* only applies when an inline-size container is available */
h2 { font-size: calc(1.2em + 1cqi); }
}
APIs
The CSSContainerRule
interface
The {{CSSContainerRule}} interface represents a ''@container'' rule.
[Exposed=Window]
interface CSSContainerRule : CSSConditionRule {
readonly attribute CSSOMString containerName;
readonly attribute CSSOMString containerQuery;
};
conditionText
of type CSSOMString
(CSSContainerRule-specific definition for attribute on CSSConditionRule)
- The
conditionText
attribute (defined on the CSSConditionRule
parent rule),
on getting, must return a value as follows:
- The ''@container'' rule has an associated <>
- The result of getting the
containerName
and
containerQuery
attributes, joined by a single whitespace.
- Otherwise
- The result of getting the
containerQuery
attribute.
containerName
of type CSSOMString
- The
containerName
attribute, on getting, must return a value as follows:
- The ''@container'' rule has an associated <>
- The result of serializing that <>.
- Otherwise
- An empty string.
containerQuery
of type CSSOMString
- The
containerQuery
attribute,
on getting, must return the <> that was specified,
without any logical simplifications,
so that the returned query will evaluate to the same result
as the specified query
in any conformant implementation of this specification
(including implementations that implement future extensions
allowed by the <> extensibility mechanism in this specification).
In other words,
token stream simplifications are allowed
(such as reducing whitespace to a single space
or omitting it in cases where it is known to be optional),
but logical simplifications (such as removal of unneeded parentheses,
or simplification based on evaluating results) are not allowed.
Issue(6205): Container Queries should have a matchContainer
method.
This will be modeled on {{matchMedia()}} and the {{MediaQueryList}} interface,
but applied to Elements rather than the Window.
When measuring layout sizes, it behaves Similar to resizeObserver
,
but it provides the additional Container Query syntax and features.
Security Considerations
No security issues have been raised against this document
Privacy Considerations
The ''font-tech()'' and ''font-format()'' functions
may provide information about the user's software
such as its version
and whether it is running with non-default settings that enable or disable certain features.
This information can also be determined through other APIs.
However, the features in this specification are one of the ways this information
is exposed on the Web.
This information can also, in aggregate, be used to improve the accuracy of
fingerprinting of the user.
Changes
-
Dimensional query containers no longer apply layout containment
(#10544)
- Add 'none'-keywords to scroll-state() features (#10874)
- Added container-type:scroll-state, and scroll-state() queries for stuck, snapped, and scrollable features
(#6402,
#10784,
#10796)
- Corrected example (there is no container-type:style)
- Specified that container queries use the flat tree (#5984)
- Moved container queries to this specification, from CSS Contain 3 (#10433)
- Imported the definitions of <font-format> and <font-tech> from CSS Fonts 4, rather than duplicating them in this specification (#8110)
- Updated to use the new parsing algorithm names and block production names
- Corrected a typo in the grammar of <font-format>
- Corrected extra spaces in the font-tech and font-format productions (#7369 )
Additions since Level 4
- Added ''@when'' and ''@else''.
- Extended [=supports queries=] to express font capabilities
via ''font-tech()'' and ''font-format()''.
- Moved Container Queries from [[CSS-CONTAIN-3]] to this specification.
(See also the [[CSS-CONTAIN-3#changes]] for more information
on the evolution of this feature.)
Acknowledgments
The ''@when'' and ''@else'' rules are based on a proposal by Tab Atkins.
Comments and previous work from
Adam Argyle,
Amelia Bellamy-Royds,
Anders Hartvoll Ruud,
Brian Kardell,
Chris Coyier,
Christopher Kirk-Nielsen,
David Herron,
Eric Portis,
Ethan Marcotte,
Florian Rivoal,
Geoff Graham,
Gregory Wild-Smith,
Ian Kilpatrick,
Jen Simmons,
Kenneth Rohde Christiansen,
Lea Verou,
Martin Auswöger,
Martine Dowden,
Mike Riethmuller,
Morten Stenshorne,
Nicole Sullivan,
Rune Lillesveen,
Scott Jehl
Scott Kellum,
Stacy Kvernmo,
Theresa O’Connor,
Una Kravets,
and many others have contributed to this specification.
at-media-001.html
at-media-002.html
at-media-003.html
at-media-content-001.html
at-media-content-002.html
at-media-content-003.html
at-media-content-004.html
at-media-dynamic-001.html
at-media-whitespace-optional-001.html
at-media-whitespace-optional-002.html
at-supports-001.html
at-supports-002.html
at-supports-003.html
at-supports-004.html
at-supports-005.html
at-supports-006.html
at-supports-007.html
at-supports-008.html
at-supports-009.html
at-supports-010.html
at-supports-011.html
at-supports-012.html
at-supports-013.html
at-supports-014.html
at-supports-015.html
at-supports-016.html
at-supports-017.html
at-supports-018.html
at-supports-019.html
at-supports-020.html
at-supports-021.html
at-supports-022.html
at-supports-023.html
at-supports-024.html
at-supports-025.html
at-supports-026.html
at-supports-027.html
at-supports-028.html
at-supports-029.html
at-supports-030.html
at-supports-031.html
at-supports-032.html
at-supports-033.html
at-supports-034.html
at-supports-035.html
at-supports-036.html
at-supports-037.html
at-supports-038.html
at-supports-039.html
at-supports-043.html
at-supports-044.html
at-supports-045.html
at-supports-046.html
at-supports-048.html
at-supports-content-001.html
at-supports-content-002.html
at-supports-content-003.html
at-supports-content-004.html
at-supports-font-format-001.html
at-supports-font-tech-001.html
at-supports-namespace-001.html
at-supports-namespace-002.html
at-supports-selector-001.html
at-supports-selector-002.html
at-supports-selector-003.html
at-supports-selector-004.html
at-supports-selector-detecting-invalid-in-logical-combinations.html
at-supports-selector-file-selector-button.html
at-supports-selector-placeholder.html
at-supports-whitespace.html
css-supports-001.xht
css-supports-002.xht
css-supports-003.xht
css-supports-004.xht
css-supports-005.xht
css-supports-006.xht
css-supports-007.xht
css-supports-008.xht
css-supports-009.xht
css-supports-010.xht
css-supports-011.xht
css-supports-012.xht
css-supports-013.xht
css-supports-014.xht
css-supports-015.xht
css-supports-016.xht
css-supports-017.xht
css-supports-018.xht
css-supports-019.xht
css-supports-020.xht
css-supports-021.xht
css-supports-022.xht
css-supports-023.xht
css-supports-024.xht
css-supports-025.xht
css-supports-026.xht
css-supports-029.xht
css-supports-030.xht
css-supports-031.xht
css-supports-032.xht
css-supports-033.xht
css-supports-034.xht
css-supports-035.xht
css-supports-036.xht
css-supports-037.xht
css-supports-038.xht
css-supports-039.xht
css-supports-040.xht
css-supports-041.xht
css-supports-042.xht
css-supports-043.xht
css-supports-044.xht
css-supports-045.xht
css-supports-046.xht
idlharness.html
js/001.html
js/CSS-supports-CSSStyleDeclaration.html
js/CSS-supports-L3.html
js/CSS-supports-L4.html
js/CSS-supports-L5.html
js/CSS-supports-selector-detecting-invalid-in-logical-combinations.html
js/conditional-CSSGroupingRule.html
js/supports-conditionText.html