使用容器大小和样式查询

Baseline 广泛可用 *

此特性已经非常成熟,可以在许多设备和浏览器版本上使用。自 2023 年 2 月起,所有主流浏览器均已支持。

* 此特性的某些部分可能存在不同级别的支持。

容器查询允许你根据特定容器的特性,将样式应用于嵌套在该容器内的元素。查询会根据查询条件对于该容器是否为真而返回 true 或 false。

容器查询类似于媒体查询@media @规则允许根据视口大小或其他设备特性来应用样式。类似地,@container @规则允许根据包含元素的大小或其他样式特性(而非视口特性)来应用样式。容器查询与媒体查询具有相同的语法规则和逻辑运算符。

css
@container <container-condition># {
  /* <stylesheet> */
}

容器查询有三种类型

容器大小查询

大小查询允许根据包含元素的当前大小来应用样式,包括方向和宽高比。包含元素需要被显式声明为大小查询容器

容器样式查询

样式查询允许根据包含元素的样式特性来应用样式。任何非空元素都可以是样式查询容器。目前,样式查询支持的唯一样式特性是 CSS 自定义属性。在这种情况下,查询会根据包含元素的自定义属性的计算值返回 true 或 false。当容器样式查询得到完全支持时,你将能够根据任何属性、声明或计算值将样式应用于任何元素的后代——例如,当容器为 display: inline flex 或具有不透明的背景颜色时。

容器滚动状态查询

滚动状态查询允许你根据滚动状态条件,选择性地将 CSS 规则应用于容器的后代,例如查询的元素是否部分滚动,或者容器是否吸附到滚动吸附容器上。包含元素需要被显式声明为滚动状态查询容器

在本指南中,我们将通过以下内容学习容器查询的基础知识:

  1. 容器大小查询,
  2. 为容器命名以限制其范围,以及
  3. @container @规则的 <container-condition> 中使用 style() 函数式表示法来创建使用自定义属性的样式查询

滚动状态查询在使用容器滚动状态查询中讨论。

容器大小查询

容器大小查询由大小条件过滤。如果容器元素已被声明为容器,并且容器条件对该元素为真,则关联的样式将应用于其包含的元素。元素的大小容器是具有包含性(containment)的最近祖先。

通过将其 container-type 属性(或 container 简写属性)设置为 sizeinline-size,元素被声明为大小查询容器

css
@container (orientation: landscape) {
  /* styles applied to descendants of this size container */
}

.sizeContainer {
  container-type: size;
}

声明大小查询容器会为其添加包含性。这是性能上的必需——一直查询 DOM 中每个元素的大小,对性能和用户体验都是不利的。此外,如果后代元素的样式改变了容器元素的大小,可能会发生无限循环。

在容器大小查询中,<container-condition> 包含一个或多个 <size-query>。每个大小查询包括一个大小特性名称、一个比较运算符和一个值。可以查询的大小特性仅限于 widthheightinline-sizeblock-sizeaspect-ratioorientation。组合一个或多个 <size-query> 的布尔语法和逻辑与 @media 大小特性查询相同。

css
form {
  container-type: inline-size;
}

@container (10em <= width <= 20em) {
  /* styles */
}

此示例中的 <container-condition> 包含一个 <size-query>——(10em <= width <= 20em)。在这种情况下,所有 <form> 元素都是任何未命名容器查询的潜在匹配项。我们容器查询中声明的样式将应用于所有宽度在 10em30em 之间(含)的表单的后代。

为容器命名

<container-condition> 可以包含一个可选的、区分大小写的 container-name。容器名称使容器条件更具体——它仅针对那些在 container-name 属性中设置了该名称的元素进行评估。

container-name 属性指定一个查询 <container-name> 值的列表,这些值可以在 @container 规则中使用;它们是区分大小写的 <ident> 值。容器名称使得可以定位元素的任何容器祖先。如果没有容器名称,查询只匹配最近的容器祖先。

css
@container [ [ <container-name> ]? <container-query> ]# {
  /* <stylesheet> */
}

在为 @container @规则添加名称后,你可以使用 container-name 属性或 container 简写属性来定位特定的容器元素。已命名的 @container @规则内的样式将仅应用于满足容器查询的、且在设置了这些名称的容器内的匹配元素。

css
@container card (orientation: landscape) {
  /* styles */
}

.todo-panel > li {
  container-type: inline-size;
  container-name: card;
}

在上面的示例中,容器查询块内的样式将应用于所有宽度大于其高度的 <li> 元素的后代。请注意,其他应用了 container-name: card 且匹配大小查询的元素,其后代元素也将应用这些样式。

css
@container wide (width >= 20em) {
  /* styles applied to descendants of wide .sizeContainer */
}

@container narrow (width < 20em) {
  /* styles applied to descendants of narrow .sizeContainer */
}

.sizeContainer {
  container-type: size;
  container-name: wide narrow;
}

在上面的示例中,该元素有两个容器名称:widenarrow。任何带有 class="sizeContainer" 的元素的后代都将应用 widenarrow 查询的样式。

默认值 container-type: normal 会阻止容器成为大小容器,但它仍然可以是样式容器。默认值 container-name: none 表示容器没有名称,但这不会阻止该元素匹配未命名的查询。

使用容器查询,我们不仅限于大小查询!你还可以查询容器的样式特性。

容器样式查询

容器样式查询是一种 @container 查询,它评估在一个或多个 style() 函数式表示法中定义的容器元素的计算样式。用于将样式特性组合成样式查询的布尔语法和逻辑与CSS 特性查询中的相同。唯一的区别是函数名称——在 <style-feature> 中是 style(),而在 <support-condition> 中是 supports()

css
@container style(<style-feature>),
    not style(<style-feature>),
    style(<style-feature>) and style(<style-feature>),
    style(<style-feature>) or style(<style-feature>) {
  /* <stylesheet> */
}

每个 style() 函数的参数是单个 <style-feature>。根据 CSS 包含性规范,<style-feature> 可以是有效的 CSS 声明、CSS 属性或 <custom-property-name>。目前唯一支持的样式特性是自定义属性,可以带值也可以不带值。请参阅浏览器兼容性表格

如果 <style-feature> 包含一个值,那么当作为 style() 参数传递的自定义属性(或将来是 CSS 声明)的计算值对于被查询的容器为真时,样式查询评估为 true。否则,它解析为 false。不带值的样式特性在计算值与给定属性的初始值不同时评估为 true。

将来,我们将能够编写如下的样式查询:

css
@container style(color: green) and style(background-color: transparent),
    not style(background-color: red),
    style(--themeBackground),
    style(--themeColor: blue) or style(--themeColor: purple),
    (width <= 100vw) and style(max-width: 600px) {
  /* <stylesheet> */
}

style() 函数式表示法用于区分样式查询和大小查询。虽然尚未支持,但我们最终将能够查询常规的 CSS 声明,如 max-width: 600px。查询 @container (max-width: 600px) 是一个大小查询;需要使用 container-typecontainer 简写属性来设置包含性。如果容器宽度为 600px 或更小,该查询将返回 true。这与查询 @container style(max-width: 600px) 不同,后者是一个样式查询;当支持时,如果容器的 max-width 值为 600px,该查询将返回 true。

在支持对常规 CSS 声明和属性的样式查询之前,我们只能将自定义属性作为 style() 的参数,可以带值也可以不带值。

css
@container style(--themeBackground),
    style(--themeColor: blue) or style(--themeColor: purple) {
  /* <stylesheet> */
}

有几点需要注意,虽然已经提到过,但仍很重要:

  • 所有元素都可以是样式查询容器;不需要设置 container-type。当后代样式不影响祖先的计算样式时,就不需要包含性。
  • <container-condition> 可以同时包含样式和大小特性。如果在查询中包含大小特性,请确保你的容器元素设置了 container-typesizeinline-size
  • 如果你不希望某个元素被视为容器,可以给它一个不会被使用的 container-name。设置 container-name: none 会移除与容器关联的任何查询名称;但这不会阻止该元素成为样式容器。
  • 在撰写本文时(2024 年 2 月),容器样式查询仅适用于 style() 查询中的 CSS 自定义属性值。

现在,让我们深入了解一下不同的 <style-feature> 类型。

自定义属性的样式查询

自定义属性的样式查询允许你查询父元素的自定义属性(也称为“CSS 变量”)。它们被包含在 <style-query> 中,就像你在特性查询中包含任何常规 CSS 属性一样:可以带值也可以不带值。

独立的自定义属性查询

style() 函数式表示法的 <style-query> 参数可以只包含一个 CSS 变量名;即一个没有值的自定义属性。当不包含值时,如果其值与 @property @规则中的 initial-value 描述符的值相同(如果存在),查询将返回 false。如果自定义属性值与 initial-value 不同,或者如果自定义属性在未注册的情况下被声明并具有任何值,则样式查询将返回 true 并匹配所有这些元素。

未注册的自定义属性

当通过 CSS 自定义属性值赋值引入 CSS 变量时,无值的自定义属性查询总是返回 true。

css
:root {
  --theme-color: rebeccapurple;
}

@container style(--theme-color) {
  /* <stylesheet> */
}

在这个例子中,容器查询匹配声明了 --theme-color 属性的元素及其所有后代。由于 CSS 变量 --theme-color 是在 :root 上声明的,样式查询 style(--theme-color) 对于该 DOM 节点内的每个元素都将为 true。

已注册的属性

已注册的自定义属性的行为有所不同。当使用 @property CSS @规则或通过 JavaScript 的 CSS.registerProperty() 显式定义时,样式查询 style(--theme-color) 仅在元素的 --theme-color 计算值与该自定义属性原始定义中设置的 initial-value 不同时才返回 true。

css
@property --theme-color {
  initial-value: rebeccapurple;
  inherits: true;
}

:root {
  --theme-color: rebeccapurple;
}

main {
  --theme-color: blue;
}

@container style(--theme-color) {
  /* <stylesheet> */
}

在这个例子中,:root 元素不匹配样式查询,因为自定义属性的值与 initial-value 的值相同。该元素(以及所有继承该值的元素)的自定义属性值仍然是 rebeccapurple。只有那些与初始值不同的元素,即本例中的 <main> 及其继承了该已更改值的后代,才会匹配。

带值的自定义属性

如果样式查询为自定义属性包含了一个值,那么该属性的元素计算值必须完全匹配,只有在自定义属性是通过包含 syntax 描述符的 @property @规则(或 CSS.registerProperty() 方法调用)定义时,等价值才算匹配。

css
@container style(--accent-color: blue) {
  /* <stylesheet> */
}

这个容器样式查询匹配任何 --accent-color 自定义属性的计算值blue 的元素。

在这种情况下,其他等同于 sRGB blue 的颜色值(例如十六进制代码 #0000ff)仅在 --accent-color 属性通过 @propertyCSS.registerProperty() 定义为颜色时才会匹配,例如:

css
@property --accent-color {
  syntax: "<color>";
  inherits: true;
  initial-value: #0000ff;
}

在这种情况下,如果 --accent-color 的值被设置为 blue#00f#0000ffrgb(0 0 255 / 1)rgb(0% 0% 100%),它都会对 @container style(--accent-color: blue) 返回 true。

示例

在这个示例中,我们有一个包含四个单选按钮的 <fieldset>。第四个选项包含一个用于输入自定义颜色的文本 <input>

html
<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 都会更新 <body> 元素上的 CSS --theme 变量的值,该元素是 <fieldset><output> 元素的祖先。当文本 <input> 更新时,只有当“其他”单选按钮被选中时,other 单选按钮的 value 才会更新,这又会更新 --theme 的值。

js
const radios = document.querySelectorAll('input[name="selection"]');
const body = document.querySelector("body");
const other = document.getElementById("other");
const color = document.getElementById("color");

for (const radio of radios) {
  radio.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);
  }
});

我们使用 @property @规则来定义一个 CSS 变量 --theme<color> 值,并设置 initial-valuered,以确保无论使用何种语法,等效的颜色都能匹配(例如,red 等于 rgb(255 0 0)#ff0000#f00)。

css
@property --theme {
  syntax: "<color>";
  inherits: true;
  initial-value: red;
}

第一个样式特性查询是一个没有值的自定义属性。当自定义属性值的计算值与该属性的 initial-value 不同时,此查询类型返回 true。在这种情况下,当 --theme 的值是任何与 red 的任何语法等价值(如 #ff0000)不同的值时,它将为 true。当为 true 时,<output> 将有一个 5px 的虚线轮廓。轮廓颜色是 --theme 的当前值。默认文本color是灰色。

css
@container style(--theme) {
  output {
    outline: 5px dotted var(--theme);
    color: #777777;
  }
}

第二个和第三个样式查询为自定义属性包含了值。如果容器的 --theme 值与列出的值是等效的颜色,即使该值与 initial-value 相同,它们也会匹配。第一个查询匹配 --theme 值等效于 redbluegreen 的元素。当匹配时,color 将是 --theme 的当前颜色值(在 bluegreen 的情况下,会覆盖第一个样式查询中设置的灰色)。

第二个样式查询指出,当 --theme 等效于 red 时,<output> 的内容也将是粗体。我们这样做是为了更好地演示容器查询是匹配的。

css
@container style(--theme: green) or style(--theme: blue) or style(--theme: red) {
  output {
    color: var(--theme);
  }
}

@container style(--theme: red) {
  output {
    font-weight: bold;
  }
}

尝试在文本框中输入不同的颜色值。你可能会注意到,与 red 等效的 sRGB 值会使 <output> 变红——因为它匹配 style(--theme: red)——同时移除了轮廓,因为如果元素的 --theme 值与 @property @规则定义的 --theme 的初始值相同,则 style(--theme) 返回 false。任何非红色的有效 sRGB 颜色值,包括 currentColorhsl(180 100% 50%) 等,都会使第一个样式查询返回 true;它们是与 initial-value 不同的值。

因为我们设置了 syntax: "<color>";,所以该 CSS 变量只能被赋予有效的 <color> 值。color 属性的有效值,如果不是 <color> 值,例如 unsetinherit,对于此自定义属性是无效的,并将被忽略。

如果你输入 unsetgibberish,JavaScript 会将 <body> 上的 style 更新为 --theme: unset--theme: gibberish。这两者都不是颜色,都是无效的并被忽略。这意味着初始值被继承且未改变,style(--theme) 返回 false,而 style(--theme: red) 返回 true。

注意:在声明自定义属性时,请考虑使用带有 syntax 描述符的 @property,以便浏览器可以正确比较计算值。

嵌套查询

容器查询可以嵌套在其他容器查询中。当所有包装的容器查询都为 true 时,在多个嵌套容器查询中定义的样式将被应用。

css
@container style(--theme: red) {
  output {
    outline: 1px dotted;
  }
  @container style(--theme: purple) {
    output {
      outline: 5px dotted;
    }
  }
}

在这种情况下,如果 <output> 嵌套在设置了 --theme: purple 的容器中,并且该容器又嵌套在 --theme 值为 red 的容器中,那么 <output> 将有一个 5px 的虚线边框。

样式查询 CSS 声明和属性

目前尚未在任何浏览器中支持,style() 函数式表示法可以包含常规的 CSS 声明,包括 CSS 属性和属性值对。

css
@container style(font-weight: bold) {
  b,
  strong {
    background: yellow;
  }
}

当支持时,这个基本示例将在父元素已经是 bold 的情况下,使任何 <b><strong> 元素的背景颜色变为黄色。

匹配是针对父容器的计算值进行的;如果父元素的计算 font-weightbold(而不是 bolder900),则匹配成功。就像自定义属性容器样式查询一样,我们不必将任何元素定义为样式容器,因为所有元素默认都是样式容器。只要一个元素没有设置 container-name,如果它设置了或继承了 font-weight: bold,它就会匹配。

查询简写属性的样式特性,如果其每个具体属性的计算值都匹配,则为 true,否则为 false。例如,如果构成该简写属性的所有 12 个具体属性(border-bottom-style 等)都设置为相同的等价值,则 @container style(border: 2px solid red) 将解析为 true。

全局 CSS 值 revertrevert-layer 作为 <style-feature> 中的值是无效的,并导致容器样式查询为 false。

不要将你在样式查询中查询的样式应用到你正在用该查询设置样式的元素上,因为这可能会导致无限循环。

预计样式查询也将在布尔上下文中接受属性。如果属性的值是该属性的初始值(即未被更改),样式查询将返回 false,否则返回 true。

css
@container style(font-weight) {
}

上面的示例将对任何 font-weight 值与其初始值不同的元素返回 true。例如,用户代理样式表为标题<th> 元素设置了 font-weight: bold。一些浏览器将 <strong><b> 设置为 bold,另一些则设置为 bolder<optgroup> 有时也会被用户代理设置非 normalfont-weight。只要元素的 font-weight 不是该用户代理的默认值,样式查询就将返回 true。

这些特性目前尚未在任何浏览器中得到支持。

规范

规范
CSS 条件规则模块第五版
# container-rule

浏览器兼容性

另见