我们最喜欢的 CSS 预处理器功能之一现已内置到该语言中:嵌套样式规则。
在嵌套之前,每个选择器都需要单独进行明确声明。这会导致重复、批量提供样式表以及分散创作体验。
.nesting { color: hotpink; } .nesting > .is { color: rebeccapurple; } .nesting > .is > .awesome { color: deeppink; }
嵌套后,选择器可以继续出现,并且与其相关的样式规则可以归为一组。
.nesting { color: hotpink; > .is { color: rebeccapurple; > .awesome { color: deeppink; } } }
通过减少重复选择器的需求,嵌套可帮助开发者更好地找到相关元素的样式规则。它还有助于样式与其目标 HTML 相匹配如果从项目中移除了上一个示例中的 .nesting
组件,您可以删除整个组,而无需在文件中搜索相关选择器实例。
嵌套有助于: - 整理 - 减小文件大小 - 重构
Chrome 112 及 Safari 技术预览版 162 中提供了嵌套功能。
CSS 嵌套使用入门
在本文的其余部分,我们将使用以下演示沙盒来帮助您直观呈现所选项。在此默认状态下,未选择任何内容,所有内容均可见。通过选择各种形状和大小,您可以练习语法并查看其实际运行效果。
沙盒包含圆形、三角形和正方形。有的为小、中或大。还有一些可能是蓝色、粉色或紫色的。它们都位于包含 .demo
的元素内。以下是您要定位的 HTML 元素的预览。
<div class="demo">
<div class="sm triangle pink"></div>
<div class="sm triangle blue"></div>
<div class="square blue"></div>
<div class="sm square pink"></div>
<div class="sm square blue"></div>
<div class="circle pink"></div>
…
</div>
嵌套示例
借助 CSS 嵌套,您可以在另一个选择器的上下文中定义某个元素的样式。
.parent {
color: blue;
.child {
color: red;
}
}
在此示例中,.child
类选择器嵌套在 .parent
类选择器中。这意味着,嵌套的 .child
选择器将仅应用于属于某个 .parent
类的元素的子元素。
也可以使用 &
符号编写此示例,以明确指明父类应放置的位置。
.parent {
color: blue;
& .child {
color: red;
}
}
这两个示例在功能上是等效的,随着本文更深入地探讨更高级的示例,您提供选项的原因也将变得更加清晰。
选择圈子
在第一个示例中,任务是添加样式,以便仅对演示中的圆圈进行淡出和模糊处理。
如果没有嵌套,CSS 如今可以:
.demo .circle {
opacity: .25;
filter: blur(25px);
}
对于嵌套,有两种有效的方法:
/* & is explicitly placed in front of .circle */
.demo {
& .circle {
opacity: .25;
filter: blur(25px);
}
}
或
/* & + " " space is added for you */
.demo {
.circle {
opacity: .25;
filter: blur(25px);
}
}
结果,.demo
内包含 .circle
类的所有元素都经过模糊处理,几乎不可见:
![彩色形状的网格中不再有圆圈,它们在背景中非常模糊。](https://developer.chrome.google.cn/static/docs/css-ui/css-nesting/image/the-colorful-grid-shapes-f1beac5a4a5be.png?authuser=5&hl=sv)
选择任意三角形和正方形
此任务需要选择多个嵌套元素,也称为组选择器。
不使用嵌套时,CSS 有两种方法:
.demo .triangle,
.demo .square {
opacity: .25;
filter: blur(25px);
}
或使用 :is()
/* grouped with :is() */
.demo :is(.triangle, .square) {
opacity: .25;
filter: blur(25px);
}
对于嵌套,有两种有效的方法:
.demo {
& .triangle,
& .square {
opacity: .25;
filter: blur(25px);
}
}
或
.demo {
.triangle, .square {
opacity: .25;
filter: blur(25px);
}
}
结果,.demo
中只剩下 .circle
个元素:
![彩色形状的网格中只有圆圈,所有其他形状几乎不可见。](https://developer.chrome.google.cn/static/docs/css-ui/css-nesting/image/the-colorful-grid-shapes-5baea09e41652.png?authuser=5&hl=sv)
选择大三角形和圆
此任务需要一个复合选择器,其中元素必须同时具有这两个类才能被选中。
如果没有嵌套,CSS 如今可以:
.demo .lg.triangle,
.demo .lg.square {
opacity: .25;
filter: blur(25px);
}
或
.demo .lg:is(.triangle, .circle) {
opacity: .25;
filter: blur(25px);
}
对于嵌套,有两种有效的方法:
.demo {
.lg.triangle,
.lg.circle {
opacity: .25;
filter: blur(25px);
}
}
或
.demo {
.lg {
&.triangle,
&.circle {
opacity: .25;
filter: blur(25px);
}
}
}
结果,所有大三角形和圆形都隐藏在 .demo
内:
![彩色网格仅可见中小形状。](https://developer.chrome.google.cn/static/docs/css-ui/css-nesting/image/the-colorful-grid-has-sm-5a9e56b5c62b4.png?authuser=5&hl=sv)
关于复合选择器和嵌套的专业提示
&
符号在此处明确显示了如何相联嵌套的选择器,因此是您的得力助手。请参考以下示例:
.demo {
.lg {
.triangle,
.circle {
opacity: .25;
filter: blur(25px);
}
}
}
虽然是一种有效的嵌套方式,但结果与您预期的元素不匹配。原因在于,如果不使用 &
来指定 .lg.triangle,
.lg.circle
复合的预期结果,实际结果将为 .lg .triangle, .lg
.circle
;后代选择器。
选择除粉色形状以外的所有形状
如果没有嵌套,CSS 如今可以:
.demo :not(.pink) {
opacity: .25;
filter: blur(25px);
}
对于嵌套,有两种有效的方法:
.demo {
:not(.pink) {
opacity: .25;
filter: blur(25px);
}
}
或
.demo {
& :not(.pink) {
opacity: .25;
filter: blur(25px);
}
}
结果,所有非粉色的形状都隐藏在 .demo
中:
![彩色网格现在是单色的,仅显示粉红色的形状。](https://developer.chrome.google.cn/static/docs/css-ui/css-nesting/image/the-colorful-grid-is-mon-0203fb32e8aea.png?authuser=5&hl=sv)
&
助您实现精准与灵活性
假设您希望使用 :not()
选择器定位 .demo
。&
需要执行以下操作:
.demo {
&:not() {
...
}
}
与上一个需要 .demo :not()
的示例不同,这会将 .demo
和 :not()
复合为 .demo:not()
。在想要嵌套 :hover
互动时,此提醒非常重要。
.demo {
&:hover {
/* .demo:hover */
}
:hover {
/* .demo :hover */
}
}
更多嵌套示例
CSS 嵌套规范中提供了更多示例。如果您希望通过示例详细了解语法,可以了解该语法包含大量有效和无效示例。
接下来的几个示例将简要介绍 CSS 嵌套功能,以帮助您了解它引入的广度。
嵌套 @media
移到样式表的其他区域,以查找修改选择器及其样式的媒体查询条件,可能会很分散读者的注意力。由于能够直接在上下文中嵌套条件,这种分心就消失了。
为方便语法,如果嵌套媒体查询只是修改当前选择器上下文的样式,则可以使用最简语法。
.card {
font-size: 1rem;
@media (width >= 1024px) {
font-size: 1.25rem;
}
}
明确使用 &
也可以:
.card {
font-size: 1rem;
@media (width >= 1024px) {
&.large {
font-size: 1.25rem;
}
}
}
此示例显示了使用 &
展开的语法,同时还定位到 .large
卡片以演示其他嵌套功能是否继续有效。
详细了解嵌套 @rules。
随处嵌套
到目前为止的所有示例都继续或附加到了之前的上下文。您可以根据需要完全更改或重新排列上下文。
.card {
.featured & {
/* .featured .card */
}
}
&
符号表示对选择器对象(而非字符串)的引用,可以放在嵌套选择器中的任意位置。它甚至可以放置多次:
.card {
.featured & & & {
/* .featured .card .card .card */
}
}
虽然这个示例看起来有点无用,但在某些情况下,能够重复选择器上下文是非常方便的。
无效的嵌套示例
有一些嵌套语法场景是无效的,如果您一直嵌套在预处理器中,则可能会令您感到惊讶。
嵌套和串联
许多 CSS 类命名惯例依赖于嵌套能够像字符串一样串联或附加选择器。这在 CSS 嵌套中不起作用,因为选择器不是字符串,而是对象引用。
.card {
&--header {
/* is not equal to ".card--header" */
}
}
如需更深入的说明,请参阅规范。
棘手的嵌套示例
嵌套在选择器列表和 :is()
中
请参考以下嵌套 CSS 代码块:
.one, #two {
.three {
/* some styles */
}
}
这是第一个以选择器列表开头,然后继续进一步嵌套的示例。前面的示例仅以选择器列表结尾。此嵌套示例中没有无效内容,但选择器列表(尤其是包含 ID 选择器的列表)内部嵌套可能存在一个棘手的实现细节。
为了使嵌套的 intent 起作用,任何不是最内部嵌套的选择器列表都将由浏览器使用 :is()
封装。此封装会维护任何编写的上下文中选择器列表的分组。这种分组的副作用是 :is(.one, #two)
,它会采用括号内选择器中最高分数的特异性。这就是 :is()
一直运行的方法,但是使用嵌套语法时可能会出乎意料,因为它并不是编写的代码。技巧总结一下:与 ID 和选择器列表进行嵌套可以生成特异性非常高的选择器。
为了清晰地回顾这个棘手的示例,我们将按如下方式将上一个嵌套块应用于文档:
:is(.one, #two) .three {
/* some styles */
}
嵌套在使用 ID 选择器的选择器列表时,请格外小心或教导 linter 警告,该选择器列表中的所有嵌套都具有较高的特异性。
混合嵌套和声明
请参考以下嵌套 CSS 代码块:
.card {
color: green;
& { color: blue; }
color: red;
}
.card
元素的颜色将是 blue
。
任何混合的样式声明都会提升到顶部,就好像它们是在发生任何嵌套之前编写的一样。如需了解更多详情,请参阅规范。
有很多方法可以解决这个问题。以下代码将三种颜色样式封装在 &
中,从而保持作者可能预期的级联顺序。.card
元素的颜色将为红色。
.card {
color: green;
& { color: blue; }
& { color: red; }
}
事实上,最好使用 &
封装所有遵循嵌套的样式。
.card {
color: green;
@media (prefers-color-scheme: dark) {
color: lightgreen;
}
& {
aspect-ratio: 4/3;
}
}
功能检测
您可以通过以下两种有效方法检测 CSS 嵌套:使用嵌套或使用 @supports
检查嵌套选择器解析功能。
使用嵌套:
html {
.has-nesting {
display: block;
}
.no-nesting {
display: none;
}
}
使用 @supports
:
@supports (selector(&)) {
/* nesting parsing available */
}
我的同事 Bramus 有一个很棒的 Codepen,它展示了此策略。
使用 Chrome 开发者工具进行调试
目前开发者工具中对嵌套的支持极少。目前,您会发现样式按预期显示在“样式”窗格中,但尚不支持跟踪嵌套及其完整的选择器上下文。我们制定了设计方案和计划来让数据清晰明了
Chrome 113 计划为 CSS 嵌套提供额外的支持。敬请期待!
未来展望
CSS 嵌套仅适用于版本 1。 版本 2 将引入更多语法糖,并可能减少需要记住的规则。很多情况都要求对嵌套解析加以限制或遇到棘手的情况。
嵌套是对 CSS 语言的一大增强。它对 CSS 的几乎所有架构方面都有影响。在有效指定版本 2 之前,需要深入探索和理解这种巨大影响。
最后,我们来查看演示,其中结合使用了 @scope
、嵌套和 @layer
。这一切都非常令人振奋!