CSS 嵌套

现在,我们最喜欢的 CSS 预处理器功能之一已内置到该语言中:嵌套样式规则。

Adam Argyle
Adam Argyle

在嵌套之前,每个选择器都需要单独进行显式声明。这会导致重复、大量样式表以及零散的创作体验。

之前
.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);
  }
}

结果,包含 .circle 类的 .demo 中的所有元素都模糊不清,几乎不可见:

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

选择任意三角形和正方形

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

没有嵌套,在目前的 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);
  }
}

结果,只有 .circle 元素保留在 .demo 中:

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

选择大三角形和圆

此任务需要一个复合选择器,其中元素必须同时存在两个类才能被选中。

不使用嵌套,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 卡片为目标,演示了其他嵌套功能仍可继续发挥作用。

详细了解如何嵌套 @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 选择器的列表。

为了让嵌套的意图起作用,浏览器会使用 :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 检查嵌套选择器解析功能。

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

使用嵌套:

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

  .no-nesting {
    display: none;
  }
}

使用 @supports

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

我的同事 Bramus 有一个出色的 Codepen,可以很好地展示此策略。

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

目前开发者工具中针对嵌套的支持极少。目前,您会发现样式在“样式”窗格中按预期表示,但尚不支持跟踪嵌套及其完整的选择器上下文。我们制定和计划 让这种行为清晰明了

Chrome 开发者工具嵌套语法的屏幕截图。

Chrome 113 计划增加对 CSS 嵌套的支持。关于以后的提供情况,请关注我们的最新消息!

未来展望

CSS 嵌套仅适用于版本 1。版本 2 会引入更多语法糖,并且可能需要记住的规则可能更少。在解析嵌套时,人们有很多需求,希望既能限制解析,又能处理复杂时刻。

嵌套是对 CSS 语言的一大改进。它对 CSS 的几乎所有架构方面都有创作方面的影响。您需要先深入探索和理解这种重大影响,然后才能有效地指定版本 2。

最后,我们来看一下这个演示,它将 @scope、嵌套和 @layer 结合使用。这一切都非常令人兴奋!

灰色背景上有一张浅色卡片。该卡片具有标题和文本、一些操作按钮和赛博朋克风格图片。