CSS Handbook · 现代进阶特性

第十一章 / 11.1 CSS 原生嵌套

第十一章 · 11.1 节

CSS 原生嵌套


过去要写出"父子结构"的样式,只能靠预处理器(Sass/Less)在编译阶段展开。如今浏览器原生支持了嵌套语法,不需要任何构建工具,直接在 .css 文件里就能按 DOM 层级组织规则,减少重复的父选择器书写。

基本嵌套语法

在一个选择器的花括号内部,可以直接写另一条以其为父级的规则:

.card {
  padding: 16px;
  border: 1px solid #ddd;

  .title {
    font-size: 18px;
    font-weight: 600;
  }

  p {
    color: #555;
  }
}
/* 等价于: */
.card { padding: 16px; border: 1px solid #ddd; }
.card .title { font-size: 18px; font-weight: 600; }
.card p { color: #555; }

子规则会自动被展开为"父选择器 + 后代选择器",无需重复书写 .card。这与 Sass 的嵌套写法几乎一致,区别在于它是浏览器原生解析的,不需要任何构建步骤。

& 符号与复合选择器

当子规则不是"后代"关系,而是需要拼接在父选择器后面(比如伪类、修饰类)时,必须显式使用 & 代表父选择器本身:

.button {
  background: teal;

  &:hover {
    background: darkcyan;
  }

  &.is-disabled {
    opacity: .5;
    pointer-events: none;
  }

  & + & {
    margin-left: 8px; /* 相邻两个 .button 之间的间距 */
  }
}
规则如果嵌套规则以标签名开头(如 p、span),会被当作后代选择器自动展开;但如果它以伪类(:hover)或类名紧跟当前元素(.is-disabled)开头,则必须写 &,否则会被解析成完全不同含义的选择器。

嵌套 @media 与 @supports

条件规则组(@media、@supports、@container)也可以直接嵌套在选择器内部,避免把同一个组件的样式拆散在文件的不同位置:

.sidebar {
  width: 280px;

  @media (max-width: 768px) {
    width: 100%;
  }
}

常见坑点

坑点说明
忘记 &紧跟当前元素的伪类/复合选择器必须显式加 &,否则会被当作后代选择器,含义完全不同
过度嵌套嵌套层级越深,生成的选择器权重越高、越难覆盖;一般建议不超过 2-3 层
与 Sass 嵌套的差异原生嵌套的选择器解析规则更严格(例如某些场景必须显式写 &),直接迁移 Sass 代码时需要逐条检查

Chapter 11 · Section 11.1

CSS Native Nesting


In the past, writing styles that mirror a "parent-child structure" required a preprocessor (Sass/Less) to expand them at build time. Browsers now support the nesting syntax natively: with no build tool at all, you can organize rules by DOM hierarchy directly in a .css file and stop repeating the parent selector.

Basic Nesting Syntax

Inside the braces of one selector, you can directly write another rule that uses it as its parent:

.card {
  padding: 16px;
  border: 1px solid #ddd;

  .title {
    font-size: 18px;
    font-weight: 600;
  }

  p {
    color: #555;
  }
}
/* equivalent to: */
.card { padding: 16px; border: 1px solid #ddd; }
.card .title { font-size: 18px; font-weight: 600; }
.card p { color: #555; }

Child rules are automatically expanded to "parent selector + descendant selector", so you never repeat .card. This is almost identical to Sass nesting, except that the browser parses it natively and no build step is needed.

The & Symbol and Compound Selectors

When a child rule is not a "descendant" relationship but has to be appended to the parent selector (for example pseudo-classes, modifier classes), you must explicitly use & to stand for the parent selector itself:

.button {
  background: teal;

  &:hover {
    background: darkcyan;
  }

  &.is-disabled {
    opacity: .5;
    pointer-events: none;
  }

  & + & {
    margin-left: 8px; /* spacing between two adjacent .button elements */
  }
}
RuleIf a nested rule starts with a tag name (such as p or span), it is automatically expanded as a descendant selector; but if it starts with a pseudo-class (:hover) or a class attached to the current element (.is-disabled), you must write &, otherwise it is parsed as a selector with a completely different meaning.

Nested @media and @supports

Conditional group rules (@media, @supports, @container) can also be nested directly inside a selector, so a component's styles don't get scattered across different parts of the file:

.sidebar {
  width: 280px;

  @media (max-width: 768px) {
    width: 100%;
  }
}

Common Pitfalls

PitfallExplanation
Forgetting &Pseudo-classes/compound selectors attached to the current element must include & explicitly, otherwise they are treated as descendant selectors with a completely different meaning
Over-nestingThe deeper the nesting, the higher the specificity of the generated selectors and the harder they are to override; generally stay within 2-3 levels
Differences from Sass nestingNative nesting parses selectors more strictly (for example, some cases require an explicit &), so migrating Sass code needs to be checked rule by rule