CSS 嵌套

我们最喜欢的 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 类的所有元素都已模糊处理,几乎不可见:

彩色形状网格不再包含圆形,它们在背景中非常微弱。
试用演示版

选择任何三角形和方形

此任务需要选择多个嵌套元素,也称为组选择器

目前,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 元素:

彩色形状网格中只剩下圆形,所有其他形状几乎不可见。
试用演示版

选择大三角形和圆形

此任务需要使用复合选择器,其中元素必须同时具有这两个类才能被选择。

不嵌套的情况下,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 中:

彩色网格中仅显示小形状和中形状。
试用演示版
有关复合选择器和嵌套的专业提示

& 符号在这里非常有用,因为它会明确显示如何联接嵌套的选择器。请参考以下示例:

.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 中:

彩色网格现在变为单色,仅显示粉色形状。
试用演示版
& 的精确性和灵活性

假设您想使用 :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 卡片,以演示其他嵌套功能仍可正常运行。

详细了解如何嵌套 @rule

在任何位置嵌套

到目前为止的所有示例都延续了或附加到了之前的上下文。您可以根据需要完全更改或重新排列上下文。

.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 */ } 

请留意,或者让 lint 工具在嵌套在使用 ID 选择器的选择器列表中时发出警告,因为该选择器列表中的所有嵌套的专属性都很高。

混合使用嵌套和声明

请考虑以下嵌套 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 检查嵌套选择器解析功能。

Bramus 的 Codepen 演示的屏幕截图,询问您的浏览器是否支持 CSS 嵌套。该问题下方有一个绿色框,表示支持。

使用嵌套:

html {   .has-nesting {     display: block;   }    .no-nesting {     display: none;   } } 

使用 @supports

@supports (selector(&)) {   /* nesting parsing available */ } 

我的同事 Bramus 提供了一个出色的 Codepen,展示了此策略。

使用 Chrome 开发者工具进行调试

开发者工具目前对嵌套的支持非常有限。目前,您会发现样式会按预期显示在“样式”窗格中,但尚不支持跟踪嵌套及其完整选择器上下文。我们有设计和计划来确保这一点。

Chrome DevTools 嵌套语法的屏幕截图。

Chrome 113 计划对 CSS 嵌套提供更多支持。敬请期待!

未来展望

CSS 嵌套仅提供版本 1。 版本 2 将引入更多语法糖,并且需要记忆的规则可能会更少。我们需要对嵌套进行解析,使其不受限制或不存在棘手问题。

嵌套是 CSS 语言的一项重大增强功能。它对 CSS 的几乎每个架构方面都有编写影响。在有效指定版本 2 之前,需要深入探索和了解这一重大影响。

最后,此处提供了一个示例,其中同时使用了 @scope、嵌套和 @layer。这一切都非常令人兴奋!

灰色背景上的浅色卡片。该卡片包含标题和文字、几个操作按钮以及一个赛博朋克风格的图片。