Using container size and style queries
Baseline 2023
Newly available
Since February 2023, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.
Container queries enable you to apply styles to elements nested within a specific container based on the features of that container. The query returns true or false depending on whether the query condition is true for the container.
Container queries are similar to media queries. The @media
at-rule enables applying styles to elements based on viewport size or other device characteristics. Similarly, the @container
at-rule enables applying styles to elements based on a containing element's size or other style features, rather than the viewport's. Container queries have the same syntax rules and logical operators as media queries.
@container <container-condition> {
/* <stylesheet> */
}
There are two types of container queries: container size queries and container style queries:
- Container size queries
-
Size queries enable applying styles to elements based on the current size of a containing element, including the orientation and aspect ratio. The containing elements need to be explicitly declared as size query containers.
- Container style queries
-
Style queries enable applying styles to elements based on a containing element's style features. Any non-empty element can be a style query container. Currently, the only style feature supported by style queries is CSS custom properties. In this case, the query returns true or false depending on the computed value of the containing element's custom properties. When container style queries are fully supported, they will enable you to apply styles to any element's descendants based on any property, declaration, or computed value — for example if the container is
display: inline flex
or has a non-transparent background color.
In this guide, we learn the basics of container queries by looking at:
- container size queries,
- naming containers to limit their scope, and
- using the
style()
functional notation within the@container
at rule's<container-condition>
to create style queries with custom properties.
Container size queries
Container size queries are filtered by a size condition. The associated styles are applied to contained elements if the container element has been declared to be a container and the container condition is true for that element. An element's size container is the nearest ancestor with containment.
Elements are declared as size query containers by setting their container-type
property (or the container
shorthand) to size
or inline-size
.
@container (orientation: landscape) {
/* styles applied to descendants of this size container */
}
.sizeContainer {
container-type: size;
}
Declaring size query containers adds containment to them. This is a performance necessity — querying the size of every element in the DOM, all the time, would be bad for performance and user experience. Additionally, if a descendant style changed the size of the container element, an infinite loop could occur.
In a container size query, the <container-condition>
includes one or more <size-query>
s. Each size query includes a size feature name, a comparison operator, and a value. The size features that can be queried are limited to width
, height
, inline-size
, block-size
, aspect-ratio
, and orientation
. The boolean syntax and logic combining one or more <size-query>
s is the same as for @media
size feature queries.
form {
container-type: inline-size;
}
@container (10em <= width <= 20em) {
/* styles */
}
The <container-condition>
in this example contains a single <size-query>
— (10em <= width <= 20em)
. In this case, all <form>
elements are potential matches for any unnamed container query. The styles declared within our container query apply to the descendants of all forms between 10em
and 30em
wide, inclusive.
Naming containers
A <container-condition>
can include an optional case-sensitive container-name
. In the form example, we could have limited the elements matched by the query by adding a name to the <container-condition>
and then setting the container-name
property with its value equal to the same name on the form elements we want to match.
The optional <container-name>
set within the query condition filters the set of query containers considered to just those with a matching query container name. The container-name
property specifies a list of query container names that can be used by @container
rules to filter which query containers are targeted. Names enable querying aspects of a specific container, even if the container is not a direct parent.
@container <container-name> <container-query> {
/* <stylesheet> */
}
Once you've added names to your @container
at rules, you can use the container-name
property or the container
shorthand to apply a space-separated list of names to container elements. Styles contained inside the named @container
at rules will only be applied to matching elements inside size query containers with the same names set on them. The <container-name>
is a case-sensitive <ident>
.
@container card (orientation: landscape) {
/* styles */
}
.cards li {
container-type: inline-size;
container-name: card;
}
This example size query is limited to elements with a container-name
of card
applied to them. In this example, the styles within the container query style block will apply to the descendants of all <li>
elements nested within an element with a class of cards
with a width that is greater than their height. Note that if there are other elements with container-name: card
applied to them that match the size query, the styles will also be applied to those elements' descendants.
@container wide (orientation: landscape) and (min-width: 20em) {
/* styles applied to descendants of .sizeContainer if size features match */
}
@container narrow (orientation: portrait) or (max-width: 20em) {
/* styles applied to descendants of .sizeContainer if size features match */
}
.sizeContainer {
container-type: size;
container-name: wide narrow;
}
In this container size query example, the element has two container names. The descendants of any elements with class="sizeContainer"
will get the styles from the wide
or narrow
query applied (or both if that element is exactly a 20em square).
Container names also enable querying styles from elements that aren't a direct parent. When a containment context is given a name, it can be specifically targeted using the @container
at-rule instead of the nearest ancestor with containment.
Set container-name: none
to prevent the container from matching any named container queries. That removes all associated container query names, but does not prevent the element from matching unnamed queries.
To prevent an element from being a size container, set container-type: normal
. This removes containment, meaning the element isn't a size container (it can still be a style container).
To prevent an element from being matched by any container queries, provide it with an unused container-name
.
article {
container-name: none;
container-type: size;
}
main {
container-name: neverUsedName;
container-type: normal;
}
In the above example, the <article>
element can match any unnamed container query. In other words, it will be tested by each @container
query that doesn't include a name in the <container-condition>
. On the other hand, assuming the neverUsedName
is never used as a container query name, the <main>
element will never be queried. Even if that name was removed, it would still not be tested against any size queries as the container-type
value of normal
means it is not a size query container.
With container queries, we are not limited to size queries! You can also query a container's style features.
Container style queries
A container style query is a @container
query that evaluates computed styles of the container element as defined in one or more style()
functional notations. The boolean syntax and logic used to combine style features into a style query are the same as in CSS feature queries. The only difference is the function name — style()
within a <style-feature>
as opposed to supports()
within a <support-condition>
:
@container style(<style-feature>),
not style(<style-feature>),
style(<style-feature>) and style(<style-feature>),
style(<style-feature>) or style(<style-feature>) {
/* <stylesheet> */
}
The parameter of each style()
function is a single <style-feature>
. Per the CSS containment specification, a <style-feature>
can be a valid CSS declaration, a CSS property, or a <custom-property-name>
. The only style feature currently supported is custom properties, with or without a value. See the browser compatibility table.
If the <style-feature>
includes a value, the style query evaluates to true if the computed value of the custom property (or, in the future, the CSS declaration) passed as the style()
argument is true for the container being queried. Otherwise, it resolves to false.
A style feature without a value evaluates to true if the computed value is different from the initial value for the given property.
In the future, we'll be able to write style queries like so:
@container style(color: green) and style(background-color: transparent),
not style(background-color: red),
style(--themeBackground),
style(--themeColor: blue) or style(--themeColor: purple),
(max-width: 100vw) and style(max-width: 600px) {
/* <stylesheet> */
}
The style()
functional notation is used to differentiate style queries from size queries. While not yet supported, we will eventually be able to query regular CSS declarations such as max-width: 100vw
. Querying @container (max-width: 100vw)
is a size query; containment with container-type
, or the container
shorthand, is needed. That query will return true if the container is 100vw or less. That is different from querying @container style(max-width: 100vw)
, which is a style query; when supported, this query will return true if the container has a max-width
value of 100vw
.
Until style queries for regular CSS declarations and properties are supported, we are limited to including only custom properties as the style()
parameter, with or without a value:
@container style(--themeBackground),
style(--themeColor: blue) or style(--themeColor: purple) {
/* <stylesheet> */
}
A few things to note that have already been mentioned but are important to remember:
- All elements can be style query containers; setting a
container-type
is not required. When descendant styles don't impact the computed styles of an ancestor, containment is not needed. - A
<container-condition>
can include both style and size features. If including size features in your query, make sure your container elements have acontainer-type
ofsize
orinline-size
set. - If you don't want an element to be considered as a container, ever, give it a
container-name
that will not be used. Settingcontainer-name: none
removes any query names associated with a container; it does not prevent the element from being a style container. - At the time of this writing (February 2024), container style queries only work with CSS custom property values in the
style()
query.
Now, let's dive in and take a look at the different <style-feature>
types.
Style queries for custom properties
Style queries for custom properties allow you to query the custom properties, also called "CSS variables", of a parent element. They are included within a <style-query>
just as you would include any regular CSS property within a feature query: either with or without a value.
Standalone custom property queries
The <style-query>
parameter of the style()
functional notation can include just a CSS variable name; a custom property with no value. When no value is included, the query will return false if the value is the same as the value of the initial-value
descriptor within the @property
at-rule, if there is one. The style query will return true and match all elements that have a custom property value that differs from the initial-value
or for all elements that have a custom property of any value if the custom property was declared without being registered.
Unregistered custom properties
When CSS variables are introduced via a simple CSS custom property value assignment, valueless custom property queries always return true.
:root {
--theme-color: rebeccapurple;
}
@container style(--theme-color) {
/* <stylesheet> */
}
In this example, the container query matches the element on which the --theme-color
property was declared and all of its descendants. As the CSS variable --theme-color
was declared on the :root
, the style query style(--theme-color)
will be true for every element within that DOM node.
Registered properties
The behavior of registered custom properties is different. When explicitly defined with the @property
CSS at-rule or via JavaScript with CSS.registerProperty()
, the style query style(--theme-color)
only returns true for elements if the element's computed value for --theme-color
is different from the initial-value
set in the original definition of that custom property.
@property --theme-color {
initial-value: rebeccapurple;
inherited: true;
}
:root {
--theme-color: rebeccapurple;
}
main {
--theme-color: blue;
}
@container style(--theme-color) {
/* <stylesheet> */
}
In this example, the :root
element does NOT match the style query because the value of the custom property is the same as the initial-value
value. The custom property value for the element (and all the elements inheriting the value) is still rebeccapurple
. Only elements that differ from the initial value, in this case, the <main>
and its descendants that inherit that changed value, are a match.
Custom property with a value
If a style query includes a value for the custom property, the element's computed value for that property must be an exact match, with equivalent values only being a match if the custom property was defined with a @property
at rule (or a CSS.registerProperty()
method call) containing a syntax
descriptor.
@container style(--accent-color: blue) {
/* <stylesheet> */
}
This container style query matches any element that has blue
as the computed_value
of the --accent-color
custom property.
In this case, other color values equivalent to sRGB blue
(such as the hexadecimal code #0000ff
) will match only if the --accent-color
property was defined as a color with @property
or CSS.registerProperty()
, for example:
@property --accent-color {
syntax: "<color>";
inherits: true;
initial-value: #00f;
}
In this case, if the value of --accent-color
were set to blue
, #00f
, #0000ff
, rgb(0 0 255 / 1)
, or rgb(0% 0% 100%)
it would return true for @container style(--accent-color: blue)
.
Example
In this example, we have a <fieldset>
with four radio buttons. The fourth option includes a text <input>
for entering a custom color.
<fieldset>
<legend>Change the value of <code>--theme</code></legend>
<ol>
<li>
<input type="radio" name="selection" value="red" id="red" />
<label for="red">--theme: red;</label>
</li>
<li>
<input type="radio" name="selection" value="green" id="green" />
<label for="green">--theme: green</label>
</li>
<li>
<input type="radio" name="selection" value="blue" id="blue" />
<label for="blue">--theme: blue</label>
</li>
<li>
<input type="radio" name="selection" value="currentcolor" id="other" />
<label for="other">Other</label>
<label for="color">color:</label>
<input text="checkbox" name="selection" value="currentcolor" id="color" />
</li>
</ol>
</fieldset>
<output>I change colors</output>
JavaScript updates the value of the CSS --theme
variable on the <body>
element, which is an ancestor of the <fieldset>
and <output>
elements, whenever a radio button is selected. When the text <input>
is updated, the value
of the other
radio button is updated only if the other
radio button is checked
, which in turn updates the value of --theme
.
const radios = document.querySelectorAll('input[name="selection"]');
const body = document.querySelector("body");
const other = document.getElementById("other");
const color = document.getElementById("color");
for (let i = 0; i < radios.length; i++) {
radios[i].addEventListener("change", (e) => {
body.style.setProperty("--theme", e.target.value);
});
}
color.addEventListener("input", (e) => {
other.style.setProperty("value", e.target.value);
if (other.checked) {
body.style.setProperty("--theme", e.target.value);
}
});
We use the @property
at-rule to define a CSS variable --theme
to be a <color>
value and set the initial-value
to #00F
, ensuring equivalent colors are a match regardless of what syntax is used (for example, #F00
is equal to rgb(255 0 0)
, #ff0000
, and red
).
@property --theme {
syntax: "<color>";
inherits: true;
initial-value: #f00;
}
The first style feature query is a custom property with no value. This query type returns true when the computed value for the custom property value is different from the initial-value
for that property. In this case, it will be true when the value of --theme
is any value other than any syntax equivalent value of #f00
( such as red
). When true, the <output>
will have a 5px dotted outline. The outline color is the current value of --theme
. The default text color
is grey.
@container style(--theme) {
output {
outline: 5px dotted var(--theme);
color: #777;
}
}
The second and third style queries include values for the custom property. These will match if the container's --theme
value is an equivalent color to the value listed, even if that value is the same as the initial-value
. The first query matches elements whose --theme
value is equivalent to red
, blue
, or green
. When it is, the color
will be the color current value of --theme
(in the case of blue
and green
, overriding the grey set in the first style query).
The second style query states that when --theme
is equivalent to red
, the <output>
's contents will also be bold. We did this to better demonstrate that the container query is a match.
@container style(--theme: green) or style(--theme: blue) or style(--theme: red) {
output {
color: var(--theme);
}
}
@container style(--theme: red) {
output {
font-weight: bold;
}
}
Try entering different color values into the text box. You may notice that values that are sRGB equivalents of red
will make the <output>
red — as it matches style(--theme: red)
— while removing the outline, because style(--theme)
returns false if the element's value for --theme
is the same as the initial value for --theme
defined by the @property
at-rule. Any non-red sRGB valid color value, including currentcolor
or hsl(180 100% 50%)
, etc., makes the first style query return true; they are values that are different from the initial-value
.
Because we set syntax: "<color>";
, the CSS variable can only be assigned valid <color>
values. Valid values for the color
property that aren't value <color>
values, such as unset
or inherit
, are invalid for this custom property, and will be ignored.
If you enter unset
or gibberish
, the JavaScript updates the style
on the <body>
to --theme: unset
or --theme: gibberish
. Neither of these are colors. Both are invalid and ignored. This means the initial value is inherited and unchanged, with style(--theme)
returning false and style(--theme: red)
returning true.
Note: When declaring custom properties, consider using @property
with the syntax
descriptor so the browser can properly compare computed values.
Nested queries
Container queries can be nested within other container queries. The styles defined inside multiple nested container queries are applied when all of the wrapping container queries are true.
@container style(--theme: red) {
output {
outline: 1px dotted;
}
@container style(--theme: purple) {
output {
outline: 5px dotted;
}
}
}
In this case, the <output>
will have a 5px dotted border if it's nested in a container where --theme: purple
is set, and that container is nested within a container whose --theme
value is red
.
Style query CSS declarations and properties
Not yet supported in any browser, the style()
functional notation can include regular CSS declarations including CSS properties and property value pairs.
@container style(font-weight: bold) {
b,
strong {
background: yellow;
}
}
When supported, this basic example will make the background color of any <b>
and <strong>
elements yellow when the parent is already bold
.
The matching is done against the computed value of the parent container; if the parent's computed font-weight
is bold
(not bolder
or 900
), there is a match. Just as with custom property container style queries, we did not have to define any elements as style containers as all elements are style containers by default. As long as an element doesn't have a container-name
set, if it has font-weight: bold
set or inherited, it will match.
Style features that query a shorthand property will be true if the computed values match for each of its longhand properties, and false otherwise. For example, @container style(
will resolve to true if all 12 longhand properties (border
: 2px solid red)border-bottom-style
, etc.) that make up that shorthand are set to the same equivalent values.
The global CSS values revert
and revert-layer
are invalid as values in a <style-feature>
and cause the container style query to be false.
Do not apply the styles you are querying in the style query to the element you are styling with that query as this may cause an infinite loop.
It is expected that style queries will also accept properties in a boolean context. The style query will return false if the value of the property is the initial value for that property (if it has not been changed), and true otherwise.
@container style(font-weight) {
}
The above example will return true for any element that has a value for font-weight
that differs from its initial value. User-agent stylesheets set font-weight: bold
for heading and <th>
elements, for example. Some browsers set <strong>
and <b>
to bold
, others to bolder
. <optgroup>
also sometimes has a font-weight
other than normal
set by the user agent. As long as the element's font-weight
is not the default value for that user-agent, the style query will return true.
These features are not yet supported in any browser.
Specifications
Specification |
---|
CSS Containment Module Level 3 # container-rule |
Browser compatibility
BCD tables only load in the browser
See also
- Media queries
- CSS
@container
at-rule - CSS
contain
property - CSS
container
shorthand property - CSS
container-name
property - Understanding
aspect-ratio
- Getting Started with Style Queries (2022)
- Style queries via una.im (2022)