使用容器大小和样式查询

基线 2023

新功能

2023 年 2 月以来,此功能在最新的设备和浏览器版本上均可使用。此功能可能不适用于旧设备或浏览器。

容器查询 使您能够根据特定容器的特性,将样式应用于嵌套在该容器内的元素。查询根据容器的查询条件是否为真,返回真或假。

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

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

容器查询有两种类型:容器大小查询容器样式查询

容器大小查询

大小查询允许根据包含元素的当前 大小 (包括方向和 纵横比)将样式应用于元素。包含元素需要显式声明为大小查询容器

容器样式查询

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

在本指南中,我们将通过以下内容来了解容器查询的基本知识:

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

容器大小查询

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

通过将元素的 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-condition> 中添加名称,然后将 container-name 属性设置为与要匹配的表单元素上的相同名称,来限制查询匹配的元素。

查询条件中设置的可选 <container-name> 将查询容器的集合过滤为仅包含匹配查询容器名称的那些容器。 container-name 属性指定一个查询容器名称列表,@container 规则可以使用该列表来过滤要定位的查询容器。即使容器不是直接父容器,名称也可以用来查询特定容器的方面。

css
@container <container-name> <container-query> {
  /* <stylesheet> */
}

在向 @container at 规则中添加名称后,可以使用 container-name 属性或 container 简写将一个空格分隔的名称列表应用于容器元素。名为 @container 的 at 规则中包含的样式将仅应用于匹配元素,这些元素位于具有相同名称设置的尺寸查询容器内的元素。

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

.cards li {
  container-type: inline-size;
  container-name: card;
}

此示例尺寸查询仅限于应用了 container-namecard 的元素。在此示例中,容器查询样式块内的样式将应用于所有 <li> 元素的后代,这些元素嵌套在具有 cards 类且宽度大于高度的元素中。请注意,如果还有其他应用了 container-name: card 的元素匹配尺寸查询,则这些样式也会应用于这些元素的后代。

css
@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;
}

在此容器尺寸查询示例中,元素有两个容器名称。任何具有 class="sizeContainer" 的元素的后代都将获得 widenarrow 查询应用的样式(如果该元素恰好是 20em 正方形,则两个样式都将应用)。

容器名称还允许从不是直接父容器的元素查询样式。当包含上下文被赋予一个名称时,可以使用 @container at 规则专门定位它,而不是使用包含的最近祖先。

设置 container-name: none 以防止容器与任何命名容器查询匹配。这将删除所有关联的容器查询名称,但不会阻止元素与未命名查询匹配。

要防止元素成为尺寸容器,请设置 container-type: normal。这将删除包含,这意味着元素不是尺寸容器(它仍然可以是 样式容器)。

要防止元素与任何容器查询匹配,请为其提供一个未使用的 container-name

css
article {
  container-name: none;
  container-type: size;
}

main {
  container-name: neverUsedName;
  container-type: normal;
}

在上面的示例中,<article> 元素可以匹配任何未命名的容器查询。换句话说,它将被每个 @container 查询测试,这些查询在 <container-condition> 中不包含名称。另一方面,假设 neverUsedName 从未用作容器查询名称,则 <main> 元素将永远不会被查询。即使该名称被删除,它仍然不会被测试与任何尺寸查询,因为 container-type 值为 normal 表示它不是尺寸查询容器。

使用容器查询,我们不仅限于尺寸查询!您还可以查询容器的样式特征。

容器样式查询

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

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 声明)的计算值为真时,样式查询将评估为真。否则,它将解析为假。没有值的样式特征将在计算值与给定属性的 初始值 不同时评估为真。

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

css
@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> */
}

style() 函数表示法用于区分样式查询和尺寸查询。虽然目前尚不支持,但我们最终将能够查询常规 CSS 声明,如 max-width: 100vw。查询 @container (max-width: 100vw) 是一个尺寸查询;需要使用 container-typecontainer 简写来进行包含。如果容器为 100vw 或更小,该查询将返回真。这与查询 @container style(max-width: 100vw) 不同,后者是一个样式查询;当支持时,此查询将在容器具有 max-width 值为 100vw 时返回真。

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

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

以下几点需要特别注意,这些要点之前已经提到过,但要记住很重要

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

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

自定义属性的样式查询

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

独立的自定义属性查询

style() 函数表示法的 <style-query> 参数可以仅包含 CSS 变量名称;一个没有值的自定义属性。当不包含值时,如果该值与 @property at 规则中的 initial-value 描述符的值相同(如果有),则查询将返回假。如果自定义属性的值不同于 initial-value,或者如果自定义属性是在没有注册的情况下声明的,则样式查询将返回真,并匹配所有具有自定义属性值的元素。

未注册的自定义属性

当通过简单的 CSS 自定义属性值赋值引入 CSS 变量时,无值自定义属性查询始终返回真。

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

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

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

注册属性

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

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

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

main {
  --theme-color: blue;
}

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

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

带值的自定义属性

如果样式查询包含自定义属性的值,则元素的该属性的计算值必须完全匹配,只有当自定义属性是使用包含 syntax 描述符的 @property at 规则(或 CSS.registerProperty() 方法调用)定义时,等效值才会匹配。

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

此容器样式查询匹配任何具有 blue 作为 --accent-color 自定义属性的 computed_value 的元素。

在这种情况下,如果 --accent-color 属性是使用 @propertyCSS.registerProperty() 作为颜色定义的,则与 sRGB blue 等效的其他颜色值(如十六进制代码 #0000ff)将匹配,例如

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

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

示例

在此示例中,我们有一个 <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 单选按钮被 checked 时,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 (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);
  }
});

我们使用 @property at 规则定义 CSS 变量 --theme<color> 值,并将 initial-value 设置为 #00F,确保无论使用何种语法,等效的颜色都是匹配的(例如,#F00 等于 rgb(255 0 0)#ff0000red)。

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

第一个样式特征查询是一个没有值的自定义属性。当自定义属性值的计算值不同于该属性的initial-value时,此查询类型返回 true。在本例中,当--theme的值为除#f00的任何语法等效值(例如red)之外的任何值时,它将为 true。当为 true 时,<output> 将具有 5 像素的虚线轮廓。轮廓颜色是--theme的当前值。默认文本color 为灰色。

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

第二个和第三个样式查询包括自定义属性的值。如果容器的--theme值与列出的值是等效的颜色,即使该值与initial-value相同,它们也将匹配。第一个查询匹配--theme值等效于redbluegreen的元素。当它为 true 时,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值为--theme的初始值,则style(--theme)返回 false,该初始值由@property at-rule 定义。任何非红色的 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。

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

嵌套查询

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

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

在本例中,如果<output>嵌套在一个--theme: purple设置的容器中,并且该容器嵌套在一个--theme值为red的容器中,则<output>将具有 5 像素的虚线边框。

样式查询 CSS 声明和属性

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

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

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

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

查询简写属性的样式特征,如果每个长写属性的计算值都匹配,则为 true,否则为 false。例如,@container style(border: 2px solid red) 如果组成该简写的 12 个长写属性(border-bottom-style 等)都设置为相同等效值,则解析为 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> 有时也具有由用户代理设置的与normal不同的font-weight。只要元素的font-weight不是该用户代理的默认值,样式查询将返回 true。

这些功能在任何浏览器中都尚不支持。

规范

规范
CSS Containment 模块级别 3
# container-rule

浏览器兼容性

BCD 表格仅在浏览器中加载

另请参阅