最近,在所有现代浏览器引擎中,查询父级的内嵌大小和容器查询单元值的功能已得到稳定支持。
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
不过,包含项规范不仅包含大小查询,它还支持查询父项的样式值。从 Chromium 111 开始,您将能够为自定义属性值应用样式包含机制,以及查询父元素以获取自定义属性的值。
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
这意味着我们可以在 CSS 中对样式进行更合乎逻辑的控制,并能够更好地将应用的逻辑和数据层与其样式分离。
CSS 包含模块级别 3 规范涵盖了尺寸和样式查询,允许从父项查询任何样式,包括 font-weight: 800
等“属性和值”对。不过,在推出此功能时,样式查询目前仅适用于 CSS 自定义属性值。这对于组合样式以及从设计中分离数据仍然非常有用。让我们来看看如何使用 CSS 自定义属性的样式查询:
样式查询使用入门
假设我们有以下 HTML:
<ul class="card-list">
<li class="card-container">
<div class="card">
...
</div>
</li>
</ul>
要使用样式查询,您必须先设置容器元素。这需要采用的方法略有不同,具体取决于您查询的是直接父级还是间接父级。
查询直接父级
与样式查询不同,您无需使用 container-type
或 container
属性将包含关系应用于 .card-container
,即可让 .card
查询其直接父级的样式。不过,我们确实需要将这些样式(在本例中为自定义属性值)应用于容器(在本例中为 .card-container
)或包含我们在 DOM 中设置其样式的元素的任何元素。我们无法针对使用该查询设置样式的直接元素应用所查询的样式,因为这可能会导致无限循环。
如需直接查询父项,您可以编写:
/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
.card {
background-color: wheat;
border-color: brown;
...
}
}
您可能已经注意到,样式查询使用 style()
封装了查询。这是为了消除尺寸值与样式之间的歧义。例如,您可以编写一个针对容器宽度的查询,格式为 @container (min-width: 200px) { … }
。如果父级容器的宽度至少为 200px,这将应用样式。不过,min-width
也可以是 CSS 属性,并且您可以使用样式查询来查询 min-width
的 CSS 值。因此,您需要使用 style()
封装容器来明确说明这一点:@container style(min-width: 200px) { … }
。
设置非直接父级的样式
如果您想查询任何不是直接父元素的样式,则需要为该元素提供 container-name
。例如,我们可以为 .card-list
提供 container-name
,并在样式查询中引用它,从而根据 .card-list
的样式将样式应用于 .card
。
/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
.card {
...
}
}
一般而言,最佳做法是为容器命名,以便明确查询内容,并解锁访问这些容器的能力。例如,您想要直接为 .card
中的元素设置样式,这可以派上用场。如果没有 .card-container
的命名容器,则无法直接查询该容器。
但所有这些在实践中都具有更大的意义。我们来看一些示例:
样式查询的实际运用
在以下情况下,样式查询特别有用:您有一个具有多个变体的可重复使用的组件,或者当您不能控制所有样式但在某些情况下需要应用更改时。此示例展示了一组共用同一卡片组件的产品卡片。某些产品卡片包含由名为 --detail
的自定义属性触发的更多详细信息/备注,例如“全新”或“低库存”。此外,如果某件商品的状态为“低库存”,则该商品会显示深红色的边框背景。此类信息可能是由服务器呈现的,可通过内嵌样式应用于卡片,如下所示:
<div class="product-list">
<div class="product-card-container" style="--detail: new">
<div class="product-card">
<div class="media">
<img .../>
<div class="comment-block"></div>
</div>
</div>
<div class="meta">
...
</div>
</div>
<div class="product-card-container" style="--detail: low-stock">
...
</div>
<div class="product-card-container">
...
</div>
...
</div>
根据这些结构化数据,您可以将值传递给 --detail
,并使用此 CSS 自定义属性来应用样式:
@container style(--detail: new) {
.comment-block {
display: block;
}
.comment-block::after {
content: 'New';
border: 1px solid currentColor;
background: white;
...
}
}
@container style(--detail: low-stock) {
.comment-block {
display: block;
}
.comment-block::after {
content: 'Low Stock';
border: 1px solid currentColor;
background: white;
...
}
.media-img {
border: 2px solid brickred;
}
}
上面的代码允许我们为 --detail: low-stock
和 --detail: new
应用条状标签,但您可能已经注意到代码块中存在一些冗余。目前,还无法只查询是否存在带有 @container style(--detail)
的 --detail
,这有助于更好地共享样式并减少重复。该工作组目前正在讨论此功能。
天气卡片
上面的示例使用具有多个可能值的单个自定义属性来应用样式。但您也可以通过使用和查询多个自定义属性来混淆处理。以下面的天气卡片为例:
要为这些卡片的背景渐变和图标设置样式,请查找天气特征,例如“多云”“雨”或“晴”:
@container style(--sunny: true) {
.weather-card {
background: linear-gradient(-30deg, yellow, orange);
}
.weather-card:after {
content: url(<data-uri-for-demo-brevity>);
background: gold;
}
}
这样,您就可以根据每张卡片的独特特征设置其样式。不过,您也可以为特征(自定义属性)组合设置样式,使用 and
组合器的方式与设置媒体查询的方式相同。例如,如果某一天既是多云天气,又是晴天,结果显示如下:
@container style(--sunny: true) and style(--cloudy: true) {
.weather-card {
background: linear-gradient(24deg, pink, violet);
}
.weather-card:after {
content: url(<data-uri-for-demo-brevity>);
background: violet;
}
}
将数据与设计分离
这两个演示都具有将数据层(在网页上呈现的 DOM)与所应用的样式分离开来在结构上带来的好处。这些样式以组件样式中的可能变体的形式编写,而端点可以发送数据,供其随后用来设置组件样式。您可以使用单个值(例如第一种情况,即更新 --detail
值),也可以使用多个变量,例如,在第二种情况下(设置 --rainy
、--cloudy
或 --sunny
)。最棒的是,您也可以组合这些值,如果同时检查 --sunny
和 --cloudy
,可能会显示局部多云样式。
无论是在设置 DOM 模型时(即在框架中构建组件时),都可以通过 JavaScript 无缝更新自定义属性值,也可以使用 <parentElem>.style.setProperty('--myProperty’, <value>)
随时进行更新。I
以下示例演示了几行代码,说明更新按钮的 --theme
,并使用样式查询和该自定义属性 (--theme
) 应用样式:
使用样式查询设置卡片的样式,用于更新自定义属性值的 JavaScript 是:
const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');
themePicker.addEventListener('input', (e) => {
btnParent.style.setProperty('--theme', e.target.value);
})
本文详细介绍的功能仅仅是个开始。容器查询可以为您提供更多功能,帮助您构建动态的自适应界面。具体到样式查询,目前还存在一些待解决的问题。一个是针对自定义属性以外的 CSS 样式实现样式查询。这已经是当前规范级别的一部分,但尚未在任何浏览器中实现。布尔上下文评估在未解决的问题解决后应添加到当前规范级别,而范围查询计划用于规范的下一级别。