1. Introduction
This is currently an early draft of the things that are new in level 5. The features in Level 3 and Level 4 are still defined in [css-conditional-3] and [css-conditional-4] and have not yet been copied here.
CSS Conditional Level 5 extends the @supports rule and supports query syntax to allow testing for supported font technologies.
It also adds an @when rule, which generalizes the concept of a conditional rule. Anything that can be expressed in an existing conditional rule can be expressed in @when by wrapping it in an appropriate function to declare what kind of condition it is. This allow authors to easily combine multiple types of queries, such as media queries and supports queries, in a single boolean expression. Without this, authors must rely on nesting separate conditional rules, which is harder to read and write, presupposes the conditions are to be conjoined with the “and” boolean relation (with no easy way to indicate anything else), and restricts their utility in the proposed conditional rule chains.
It also adds @else rules, which immediately follow other conditional rules and automatically qualify their conditions as the inverse of the immediately preceding rule’s conditions, such that only the first matching rule in a conditional rule chain is applied.
It also adds Container Queries. They are conceptually similar to Media Queries, but allow testing aspects of elements within the document (such as box dimensions or computed styles), rather than on the document as a whole.
2. Extensions to the @supports rule
This level of the specification extends the <supports-feature> syntax as follows:
<supports-feature> = <supports-selector-fn> | <supports-font-tech-fn> | <supports-font-format-fn> | <supports-decl> <supports-font-tech-fn> = font-tech( <font-tech> ) <supports-font-format-fn> = font-format( <font-format> )
Tests
- <supports-font-tech-fn>
-
The result is true if the UA supports the font tech provided as an argument to the function.
- <supports-font-format-fn>
-
The result is true if the UA supports the font format provided as an argument to the function.
2.1. Extensions to the definition of support
A CSS processor is considered to support a font tech when it is capable of utilizing the specified CSS Fonts 4 § 11.1 Font tech in layout and rendering.
A CSS processor is considered to support a font format when it is capable of utilizing the specified CSS Fonts 4 § 11.2 Font formats in layout and rendering, and this format is not specified as a <string>.
3. Generalized Conditional Rules: the @when rule
The @when at-rule is a conditional group rule that generalizes the individual conditional group rules such as @media and @supports. It is defined as:
@when <boolean-condition>{ <rule-list>}
Where <boolean-condition> is a boolean algebra a la Media Queries 4 § 3 Syntax, but with media() and supports() functions as leaves.
Define "boolean algebra, with X as leaves" in a generic way in Conditional, so all the conditional rules can reference it directly, rather than having to redefine boolean algebra on their own.
The media() and supports() functions are defined as:
media () =media ( [ <mf-plain> | <mf-boolean> | <mf-range>] ) supports () =supports ( <declaration>)
A media() or supports() function is associated the boolean result that its contained condition is associated with.
4. Chained Conditionals: the @else rule
Usually, conditional group rules are independent; each one has a separate condition evaluated without direct reference to any other rule, and decides whether or not to apply its contained rules based solely on its condition.
This is fine for simple conditions, but makes it difficult to write a collection of conditionals that are meant to be mutually exclusive: authors have to very carefully craft their conditions to not activate when the other rules are meant to, and make sure the collection of conditionals don’t accidentally all exclude some situation which is then left unstyled.
The @else rule is a conditional group rule used to form conditional rule chains, which associate multiple conditional rules and guarantee that only the first one that matches will evaluate its condition as true. It is defined as:
@else <boolean-condition>?{ <rule-list>}
@else is interpreted identically to @when. If its <boolean-condition> 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.
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.
@when media ( width >=400 px ) andmedia ( pointer: fine) andsupports ( display: flex) { /* A */ } @else supports ( caret-color: pink) andsupports ( 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 >=400 px ) 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 >=400 px ) 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.
The fallback has no test condition, so will always be chosen unless one of the earlier conditions succeeds.
@when font-tech ( color-COLRv1) andfont-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.
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 ); } }
5. 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.
main, aside{ container : my-layout / inline-size; } .media-object{ display : grid; grid-template : 'img' auto'content' auto /100 % ; } @container my-layout( inline-size >45 em ) { .media-object{ grid-template : 'img content' auto / auto1 fr ; } }
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.
-
::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
< style > # container { width : 100 px ; container-type : inline - size ; } @ container ( inline-size < 150px ) { # inner :: before { content : "BEFORE" ; } } </ style > < div id = container > < span id = inner ></ span > </ div >
< div id = host style = "width:200px" > < template shadowroot = open > < style > # container { width : 100 px ; 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 >
5.1. Creating Query Containers: the container-type property
Name: | container-type |
---|---|
Value: | normal | [ [ size | inline-size ] || scroll-state ] |
Initial: | normal |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Computed value: | specified keyword |
Canonical order: | per grammar |
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 and block axis. Applies layout containment, style containment, and size containment to the principal box.
- inline-size
- Establishes a query container for container size queries on the container’s own inline axis. Applies layout containment, style containment, and inline-size containment to the principal box.
- 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.
aside, main{ container-type : inline-size; } h2{ font-size : 1.2 em ; } @container ( width >40 em ) { h2{ font-size : 1.5 em ; } }
The 40em value used in the query condition is relative to the computed value of font-size on the relevant query container.
@container style ( --cards: small) { article{ border : thin solid silver; border-radius : 0.5 em ; padding : 1 em ; } }
#sticky{ container-type : scroll-state; position : sticky; } @container scroll-state ( stuck: top) { #sticky-child{ background-color : lime; } }
5.2. Naming Query Containers: the container-name property
Name: | container-name |
---|---|
Value: | none | <custom-ident>+ |
Initial: | none |
Applies to: | all elements |
Inherited: | no |
Percentages: | n/a |
Computed value: | the keyword none, or an ordered list of identifiers |
Canonical order: | per grammar |
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.
- <custom-ident>
- Specifies a query container name as an identifier. The keywords none, and, not, and or are excluded from this <custom-ident>.
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 >12 em ) { .card{ margin-block : 2 em ; } } @container my-component-library( inline-size >30 em ) { .card{ margin-inline : 2 em ; } }
It is also possible to query for a container only based on its name.
@container my-page-layout{ .card{ padding : 1 em ; } }
5.3. Creating Named Containers: the container shorthand
Name: | container |
---|---|
Value: | <'container-name'> [ / <'container-type'> ]? |
Initial: | see individual properties |
Applies to: | see individual properties |
Inherited: | see individual properties |
Percentages: | see individual properties |
Computed value: | see individual properties |
Animation type: | see individual properties |
Canonical order: | per grammar |
Tests
- animation-container-size.html (live test) (source)
- animation-container-type-dynamic.html (live test) (source)
- animation-nested-animation.html (live test) (source)
- animation-nested-transition.html (live test) (source)
- aspect-ratio-feature-evaluation.html (live test) (source)
- at-container-parsing.html (live test) (source)
- at-container-serialization.html (live test) (source)
- at-container-style-parsing.html (live test) (source)
- at-container-style-serialization.html (live test) (source)
- auto-scrollbars.html (live test) (source)
- backdrop-invalidation.html (live test) (source)
- calc-evaluation.html (live test) (source)
- canvas-as-container-001.html (live test) (source)
- canvas-as-container-002.html (live test) (source)
- canvas-as-container-003.html (live test) (source)
- canvas-as-container-004.html (live test) (source)
- canvas-as-container-005.html (live test) (source)
- canvas-as-container-006.html (live test) (source)
- change-display-in-container.html (live test) (source)
- chrome-legacy-skip-recalc.html (live test) (source)
- column-spanner-in-container.html (live test) (source)
- conditional-container-status.html (live test) (source)
- container-computed.html (live test) (source)
- container-for-cue.html (live test) (source)
- container-for-shadow-dom.html (live test) (source)
- container-inheritance.html (live test) (source)
- container-inner-at-rules.html (live test) (source)
- container-inside-multicol-with-table.html (live test) (source)
- container-longhand-animation-type.html (live test) (source)
- container-name-computed.html (live test) (source)
- container-name-invalidation.html (live test) (source)
- container-name-parsing.html (live test) (source)
- container-name-tree-scoped.html (live test) (source)
- container-nested.html (live test) (source)
- container-parsing.html (live test) (source)
- container-selection-unknown-features.html (live test) (source)
- container-selection.html (live test) (source)
- container-size-invalidation-after-load.html (live test) (source)
- container-size-invalidation.html (live test) (source)
- container-size-nested-invalidation.html (live test) (source)
- container-size-shadow-invalidation.html (live test) (source)
- container-type-computed.html (live test) (source)
- container-type-containment.html (live test) (source)
- container-type-invalidation.html (live test) (source)
- container-type-layout-invalidation.html (live test) (source)
- container-type-parsing.html (live test) (source)
- container-units-animation.html (live test) (source)
- container-units-basic.html (live test) (source)
- container-units-computational-independence.html (live test) (source)
- container-units-content-box.html (live test) (source)
- container-units-gradient-invalidation.html (live test) (source)
- container-units-gradient.html (live test) (source)
- container-units-in-at-container-dynamic.html (live test) (source)
- container-units-in-at-container-fallback.html (live test) (source)
- container-units-in-at-container.html (live test) (source)
- container-units-ineligible-container.html (live test) (source)
- container-units-invalidation.html (live test) (source)
- container-units-media-queries.html (live test) (source)
- container-units-rule-cache.html (live test) (source)
- container-units-selection.html (live test) (source)
- container-units-shadow.html (live test) (source)
- container-units-sharing-via-rule-node.html (live test) (source)
- container-units-small-viewport-fallback.html (live test) (source)
- container-units-svglength.html (live test) (source)
- container-units-typed-om.html (live test) (source)
- counters-flex-circular.html (live test) (source)
- counters-in-container-dynamic.html (live test) (source)
- counters-in-container.html (live test) (source)
- br-crash.html (live test) (source)
- canvas-as-container-crash.html (live test) (source)
- chrome-bug-1289718-000-crash.html (live test) (source)
- chrome-bug-1289718-001-crash.html (live test) (source)
- chrome-bug-1346969-crash.html (live test) (source)
- chrome-bug-1362391-crash.html (live test) (source)
- chrome-bug-1429955-crash.html (live test) (source)
- chrome-bug-1505250-crash.html (live test) (source)
- chrome-bug-346264227-crash.html (live test) (source)
- chrome-bug-372358471-crash.html (live test) (source)
- chrome-custom-highlight-crash.html (live test) (source)
- chrome-layout-root-crash.html (live test) (source)
- chrome-quotes-crash.html (live test) (source)
- chrome-remove-insert-evaluator-crash.html (live test) (source)
- columns-in-table-001-crash.html (live test) (source)
- columns-in-table-002-crash.html (live test) (source)
- container-in-canvas-crash.html (live test) (source)
- container-type-change-chrome-legacy-crash.html (live test) (source)
- dialog-backdrop-crash.html (live test) (source)
- dirty-rowgroup-crash.html (live test) (source)
- flex-in-columns-000-crash.html (live test) (source)
- flex-in-columns-001-crash.html (live test) (source)
- flex-in-columns-002-crash.html (live test) (source)
- flex-in-columns-003-crash.html (live test) (source)
- focus-inside-content-visibility-crash.html (live test) (source)
- force-sibling-style-crash.html (live test) (source)
- grid-in-columns-000-crash.html (live test) (source)
- grid-in-columns-001-crash.html (live test) (source)
- grid-in-columns-002-crash.html (live test) (source)
- grid-in-columns-003-crash.html (live test) (source)
- iframe-init-crash.html (live test) (source)
- inline-multicol-inside-container-crash.html (live test) (source)
- inline-with-columns-000-crash.html (live test) (source)
- inline-with-columns-001-crash.html (live test) (source)
- input-column-group-container-crash.html (live test) (source)
- input-placeholder-inline-size-crash.html (live test) (source)
- marker-gcs-after-disconnect-crash.html (live test) (source)
- math-block-container-child-crash.html (live test) (source)
- mathml-container-type-crash.html (live test) (source)
- orthogonal-replaced-crash.html (live test) (source)
- pseudo-container-crash.html (live test) (source)
- remove-dom-child-change-style.html (live test) (source)
- reversed-ol-crash.html (live test) (source)
- size-change-during-transition-crash.html (live test) (source)
- svg-layout-root-crash.html (live test) (source)
- svg-resource-in-container-crash.html (live test) (source)
- svg-text-crash.html (live test) (source)
- table-in-columns-000-crash.html (live test) (source)
- table-in-columns-001-crash.html (live test) (source)
- table-in-columns-002-crash.html (live test) (source)
- table-in-columns-003-crash.html (live test) (source)
- table-in-columns-004-crash.html (live test) (source)
- table-in-columns-005-crash.html (live test) (source)
- top-layer-crash.html (live test) (source)
- top-layer-nested-crash.html (live test) (source)
- custom-layout-container-001.https.html (live test) (source)
- custom-property-style-queries.html (live test) (source)
- custom-property-style-query-change.html (live test) (source)
- deep-nested-inline-size-containers.html (live test) (source)
- dialog-backdrop-create.html (live test) (source)
- dialog-backdrop-remove.html (live test) (source)
- display-contents-dynamic-style-queries.html (live test) (source)
- display-contents.html (live test) (source)
- display-in-container.html (live test) (source)
- display-none.html (live test) (source)
- fieldset-legend-change.html (live test) (source)
- flex-basis-with-container-type.html (live test) (source)
- font-relative-calc-dynamic.html (live test) (source)
- font-relative-units-dynamic.html (live test) (source)
- font-relative-units.html (live test) (source)
- fragmented-container-001.html (live test) (source)
- get-animations.html (live test) (source)
- grid-container.html (live test) (source)
- grid-item-container.html (live test) (source)
- idlharness.html (live test) (source)
- iframe-in-container-invalidation.html (live test) (source)
- iframe-invalidation.html (live test) (source)
- ineligible-containment.html (live test) (source)
- inheritance-from-container.html (live test) (source)
- inline-size-and-min-width.html (live test) (source)
- inline-size-bfc-floats.html (live test) (source)
- inline-size-containment-vertical-rl.html (live test) (source)
- inline-size-containment.html (live test) (source)
- inner-first-line-non-matching.html (live test) (source)
- layout-dependent-focus.html (live test) (source)
- multicol-container-001.html (live test) (source)
- multicol-inside-container.html (live test) (source)
- nested-query-containers.html (live test) (source)
- nested-size-style-container-invalidation.html (live test) (source)
- never-match-container.html (live test) (source)
- no-layout-containment-abspos-dynamic.html (live test) (source)
- no-layout-containment-abspos.html (live test) (source)
- no-layout-containment-baseline.html (live test) (source)
- no-layout-containment-fixedpos-dynamic.html (live test) (source)
- no-layout-containment-fixedpos.html (live test) (source)
- no-layout-containment-scroll.html (live test) (source)
- no-layout-containment-subgrid-crash.html (live test) (source)
- orthogonal-wm-container-query.html (live test) (source)
- percentage-padding-orthogonal.html (live test) (source)
- pseudo-elements-001.html (live test) (source)
- pseudo-elements-002.html (live test) (source)
- pseudo-elements-002b.html (live test) (source)
- pseudo-elements-003.html (live test) (source)
- pseudo-elements-004.html (live test) (source)
- pseudo-elements-005.html (live test) (source)
- pseudo-elements-006.html (live test) (source)
- pseudo-elements-007.html (live test) (source)
- pseudo-elements-008.html (live test) (source)
- pseudo-elements-009.html (live test) (source)
- pseudo-elements-010.html (live test) (source)
- pseudo-elements-011.html (live test) (source)
- pseudo-elements-012.html (live test) (source)
- pseudo-elements-013.html (live test) (source)
- query-content-box.html (live test) (source)
- query-evaluation-style.html (live test) (source)
- query-evaluation.html (live test) (source)
- reattach-container-with-dirty-child.html (live test) (source)
- registered-color-style-queries.html (live test) (source)
- resize-while-content-visibility-hidden.html (live test) (source)
- at-container-overflowing-parsing.html (live test) (source)
- at-container-overflowing-serialization.html (live test) (source)
- at-container-snapped-parsing.html (live test) (source)
- at-container-snapped-serialization.html (live test) (source)
- at-container-stuck-parsing.html (live test) (source)
- at-container-stuck-serialization.html (live test) (source)
- container-type-scroll-state-computed.html (live test) (source)
- container-type-scroll-state-containment.html (live test) (source)
- container-type-scroll-state-parsing.html (live test) (source)
- scroll-state-initially-overflowing.html (live test) (source)
- scroll-state-initially-snapped.html (live test) (source)
- scroll-state-initially-stuck.html (live test) (source)
- scroll-state-overflowing-change.html (live test) (source)
- scroll-state-overflowing-container-type-change.html (live test) (source)
- scroll-state-overflowing-layout-change.html (live test) (source)
- scroll-state-overflowing-wm.html (live test) (source)
- scroll-state-snapped-change.html (live test) (source)
- scroll-state-snapped-container-type-change.html (live test) (source)
- scroll-state-snapped-layout-change.html (live test) (source)
- scroll-state-snapped-none.html (live test) (source)
- scroll-state-snapped-snap-changing.html (live test) (source)
- scroll-state-snapped-wm.html (live test) (source)
- scroll-state-stuck-container-type-change.html (live test) (source)
- scroll-state-stuck-layout-change.html (live test) (source)
- scroll-state-stuck-writing-direction.html (live test) (source)
- scroll-state-target-query-change.html (live test) (source)
- scrollbar-container-units-block.html (live test) (source)
- scrollbar-container-units-inline.html (live test) (source)
- sibling-layout-dependency.html (live test) (source)
- size-container-no-principal-box.html (live test) (source)
- size-container-with-quotes.html (live test) (source)
- size-container-writing-mode-change.html (live test) (source)
- size-feature-evaluation.html (live test) (source)
- style-change-in-container.html (live test) (source)
- style-container-for-shadow-dom.html (live test) (source)
- style-container-invalidation-inheritance.html (live test) (source)
- style-not-sharing-float.html (live test) (source)
- style-query-document-element.html (live test) (source)
- style-query-no-cycle.html (live test) (source)
- style-query-with-unknown-width.html (live test) (source)
- svg-foreignobject-child-container.html (live test) (source)
- svg-foreignobject-no-size-container.html (live test) (source)
- svg-g-no-size-container.html (live test) (source)
- svg-root-size-container.html (live test) (source)
- table-inside-container-changing-display.html (live test) (source)
- top-layer-dialog-backdrop.html (live test) (source)
- top-layer-dialog-container.html (live test) (source)
- top-layer-dialog.html (live test) (source)
- top-layer-nested-dialog.html (live test) (source)
- transition-scrollbars.html (live test) (source)
- transition-style-change-event-002.html (live test) (source)
- transition-style-change-event.html (live test) (source)
- unsupported-axis.html (live test) (source)
- viewport-units-dynamic.html (live test) (source)
- viewport-units.html (live test) (source)
- whitespace-update-after-removal.html (live test) (source)
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.
main{ container : my-layout / size; } .grid-item{ container : my-component / inline-size; }
5.4. 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-contents> block of an @container rule are 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 <container-condition>#{ <block-contents>}
where:
<container-condition> =[ <container-name>? <container-query>?] ! <container-name> = <custom-ident> <container-query> = not <query-in-parens> | <query-in-parens>[ [ and <query-in-parens>] * |[ or <query-in-parens>] *] <query-in-parens> =( <container-query>) |( <size-feature>) |style ( <style-query>) |scroll-state ( <scroll-state-query>) | <general-enclosed> <style-query> = not <style-in-parens> | <style-in-parens>[ [ and <style-in-parens>] * |[ or <style-in-parens>] *] | <style-feature> <style-in-parens> =( <style-query>) |( <style-feature>) | <general-enclosed> <scroll-state-query> = not <scroll-state-in-parens> | <scroll-state-in-parens>[ [ and <scroll-state-in-parens>] * |[ or <scroll-state-in-parens>] *] | <scroll-state-feature> <scroll-state-in-parens> =( <scroll-state-query>) |( <scroll-state-feature>) | <general-enclosed>
The keywords none, and, not, and or are excluded from the <custom-ident> 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 <container-query>. If the <container-query> contains unknown or unsupported container features, no query container will be selected for that <container-condition>. The <container-name> 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 <container-query> 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, <general-enclosed> evaluates to unknown. If the <container-query> is omitted, the query container is eligible as long as the <container-name> matches.
If a container query includes multiple <container-condition>s, each condition will select it’s own query container, and evaluate independently. A container query is true if any of its component <container-condition>s are true, and false only if all of its component <container-condition>s are false.
@container card( inline-size >30 em ) andstyle ( --responsive: true) { /* styles */ }
The styles above will only be applied if there is an ancestor container named "card" that meets both the inline-size and style conditions.
We can also combine multiple conditions into a list, with each condition evaluating against a different container:
@container card( inline-size >30 em ), style ( --large: true) { /* styles */ }
The styles above will be applied if there is an ancestor container named "card" that meets the inline-size condition or the nearest style container meets the 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 <container-condition>s into a single query.
@container card( inline-size >30 em ), style ( --responsive: true) { /* styles */ }
The styles above will apply for an element inside either a container named "card" that meets the inline-size condition, or a container meeting the 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 >30 em ) { @container style ( --responsive: true) { /* styles */ } }
The styles above will only be applied if there is both an ancestor container named "card" that meets the inline-size condition, and an ancestor container meeting the 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.
5.5. 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 animation effects.
main{ display : flex; width : 300 px ; } #container{ container-type : inline-size; flex : 1 ; } /* Resolved width is initially 200px, but changes as the transition on #sibling progresses. */ #inner{ transition : 1 s background-color; background-color : tomato; } /* When this container query starts (or stops) applying, a transition must start on background-color on #inner. */ @container ( width <=150 px ) { #inner{ background-color : skyblue; } } #sibling{ width : 100 px ; transition : width1 s ; } #sibling:hover{ width : 200 px ; }
< 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.
6. 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.
6.1. 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 (<size-feature>) that each query a single, specific dimensional feature of the query container. The syntax of a <size-feature> 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 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.
aside, main{ container-type : inline-size; } aside{ font-size : 16 px ; } main{ font-size : 24 px ; } @container ( width >40 em ) { h2{ font-size : 1.5 em ; } }
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.
aside, main{ container-type : inline-size; } aside{ --query : 300 px ; } main{ --query : 500 px ; } @container ( width >var ( --query)) { h2{ font-size : 1.5 em ; } }
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.
6.1.1. Width: the width feature
Name: | width |
---|---|
For: | @container |
Value: | <length> |
Type: | range |
The width container feature queries the width of the query container’s content box.
6.1.2. Height: the height feature
Name: | height |
---|---|
For: | @container |
Value: | <length> |
Type: | range |
The height container feature queries the height of the query container’s content box.
6.1.3. Inline-size: the inline-size feature
Name: | inline-size |
---|---|
For: | @container |
Value: | <length> |
Type: | range |
The inline-size container feature queries the size of the query container’s content box in the query container’s inline axis.
6.1.4. Block-size: the block-size feature
Name: | block-size |
---|---|
For: | @container |
Value: | <length> |
Type: | range |
The block-size container feature queries the size of the query container’s content box in the query container’s block axis.
6.1.5. Aspect-ratio: the aspect-ratio feature
Name: | aspect-ratio |
---|---|
For: | @container |
Value: | <ratio> |
Type: | range |
The aspect-ratio container feature is defined as the ratio of the value of the width container feature to the value of the height container feature.
6.1.6. Orientation: the orientation feature
Name: | orientation |
---|---|
For: | @container |
Value: | portrait | landscape |
Type: | discrete |
- portrait
- The orientation container feature is portrait when the value of the height container feature is greater than or equal to the value of the width container feature.
- landscape
- Otherwise orientation is landscape.
6.2. 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 (<style-feature>) that each query a single, specific property of the query container. The syntax of a <style-feature> is either the same as for a valid declaration [CSS-SYNTAX-3], a supported CSS property, or a <custom-property-name>.
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 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 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 with respect to the query container, the same as other values.
6.3. Scroll State Container Features
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 (<scroll-state-feature>) that each query a single feature of the query container. The syntax of a <scroll-state-feature> 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 overflowing feature, snapped the latter.
6.3.1. Updating Scroll State
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.
6.3.2. Sticky positioning: the stuck feature
Name: | stuck |
---|---|
For: | @container |
Value: | none | top | right | bottom | left | block-start | inline-start | block-end | inline-end |
Type: | discrete |
The stuck container feature queries whether a 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.
@container scroll-state (( stuck: top) and( stuck: left)) { ...}
Will never match:
@container scroll-state (( stuck: left) and( stuck: right)) { ...}
- none
- The sticky container is not shifted in any direction.
- top
- The sticky container is shifted to stay inside the top edge.
- right
- The sticky container is shifted to stay inside the right edge.
- bottom
- The sticky container is shifted to stay inside the bottom edge.
- left
- The sticky container is shifted to stay inside the left edge.
- block-start
- The sticky container is shifted to stay inside the block-start edge.
- inline-start
- The sticky container is shifted to stay inside the inline-start edge.
- block-end
- The sticky container is shifted to stay inside the block-end edge.
- inline-end
- The sticky container is shifted to stay inside the inline-end edge.
6.3.3. Scroll snapping: the snapped feature
Name: | snapped |
---|---|
For: | @container |
Value: | none | x | y | block | inline |
Type: | discrete |
The 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
- snapped container feature matches x if the query container is a horizontal snap target for its scroll container.
- y
- snapped container feature matches y if the query container is a vertical snap target for its scroll container.
- block
- 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
- 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.
6.3.4. Overflowing: the overflowing feature
Name: | overflowing |
---|---|
For: | @container |
Value: | none | top | right | bottom | left | block-start | inline-start | block-end | inline-end |
Type: | discrete |
The overflowing 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, overflowing does not match for a 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.
7. 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:
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.
/* The fallback value does not rely on containment */ h2{ font-size : 1.2 em ; } @container ( inline-size >=0 px ) { /* only applies when an inline-size container is available */ h2{ font-size : calc ( 1.2 em +1 cqi ); } }
8. APIs
8.1. 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 typeCSSOMString
(CSSContainerRule-specific definition for attribute on CSSConditionRule)-
The
conditionText
attribute (defined on theCSSConditionRule
parent rule), on getting, must return a value as follows:- The @container rule has an associated <container-name>
- The result of getting the
containerName
andcontainerQuery
attributes, joined by a single whitespace. - Otherwise
- The result of getting the
containerQuery
attribute.
containerName
of typeCSSOMString
-
The
containerName
attribute, on getting, must return a value as follows:- The @container rule has an associated <container-name>
- The result of serializing that <container-name>.
- Otherwise
- An empty string.
containerQuery
of typeCSSOMString
- The
containerQuery
attribute, on getting, must return the <container-query> 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 <general-enclosed> 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.
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. [Issue #6205]
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
Changes since the Working Draft of 23 July 2024
- Add none-keywords to scroll-state() features (#10874)
- Added container-type:scroll-state, and scroll-state() queries for stuck, snapped, and overflowing features (#6402, #10784, #10796)
- Corrected example (there is no container-type:style)
- Specified that container queries use the flat tree (#5984)
Changes since the First Public Working Draft of 21 December 2021
- 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 Containment 3 § A 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.