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);
  }
}

结果:仅保留 .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 */
}

请注意,或者教您的 linter 在选择器列表中使用 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 开发者工具嵌套语法的屏幕截图。

Chrome 113 计划提供对 CSS 嵌套的额外支持。敬请期待。

未来展望

CSS 嵌套目前仅为版本 1。 版本 2 将引入更多语法糖,并可能减少需要记忆的规则。人们非常希望嵌套的解析不受限制或出现棘手的情况。

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

最后,这里有一个演示 ,它同时使用了 @scope、嵌套和 @layer。一切都非常令人兴奋!

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