CSS 嵌套
变量给了值名字。calc 等函数算出了结果。Nesting 整理的是使用这些值的选择器:把卡片的标题、按钮和 hover 样式写在 .card 里面,而不是在整份文件里重复父选择器名。
嵌套子选择器
把一条规则写在另一条规则里面。嵌套的 class 或类型选择器会以后代方式接到父选择器上(空格组合器),除非你用 & 另作说明:
.card {
padding: 1.25rem;
border-radius: 0.75rem;
background: var(--surface, #fff);
.title {
margin: 0 0 0.5rem;
font-size: 1.25rem;
}
.body {
color: var(--muted, #444);
line-height: 1.5;
}
}
浏览器会把它展开成 .card .title 和 .card .body。你得到的仍是普通后代选择器 — nesting 是结构和就近性的语法糖,不是新的匹配模型。
父选择器 &
当嵌套规则必须挂在与父规则同一个元素上时 — 伪类、伪元素,或父元素上的额外 class — 使用 &:
.button {
display: inline-flex;
padding: 0.6rem 1.1rem;
border: 0;
border-radius: 0.5rem;
background: var(--brand, #2563eb);
color: #fff;
&:hover {
background: var(--brand-hover, #1d4ed8);
}
&:focus-visible {
outline: 2px solid var(--brand, #2563eb);
outline-offset: 2px;
}
&.is-primary {
background: var(--brand-strong, #1e40af);
}
}
&:hover 就是 .button:hover。&.is-primary 就是 .button.is-primary。没有 & 时,裸写 :hover { } 在原生 CSS nesting 中无效 — & 让附着关系明确。
带 & 的组合器
当你要的是子元素、兄弟或其他显式关系 — 而不是普通的后代空格 — 把组合器写在 & 后面:
.nav {
display: flex;
gap: 0.75rem;
& > .item {
padding: 0.35rem 0.6rem;
}
& > .item + .item {
border-inline-start: 1px solid #ddd;
}
/* same as nesting .item without & — descendant */
.badge {
font-size: 0.75rem;
}
}
& > .item → .nav > .item。不带 & 的嵌套 .badge → .nav .badge。要有意识地选择:无意中嵌出的深层后代是常见的 nesting 坏味道。
把 @media 嵌在组件旁
可以把 @media 这类 at-rule 嵌进样式规则里,让断点紧挨着它们要改的组件:
.hero {
padding: 2rem 1.25rem;
h1 {
font-size: clamp(1.75rem, 1.2rem + 2vw, 2.75rem);
}
@media (min-width: 48rem) {
padding: 3rem 2rem;
h1 {
max-width: 20ch;
}
}
}
展开后,媒体查询仍然包裹属于 .hero(以及 .hero h1)的声明。把它和前面 Modern Toolkit 课里的 token、clamp() 搭配使用 — nesting 只是搬动规则的位置。
嵌套模式速览
📊 原生 nesting 速查
| 你写 | 含义 | 何时用 |
|---|---|---|
.card { .title { } } |
.card .title |
为组件下的后代设样式 |
.btn { &:hover { } } |
.btn:hover |
同一元素上的伪类 |
.btn { &.big { } } |
.btn.big |
同一元素上的额外 class |
.nav { & > a { } } |
.nav > a |
显式子组合器 |
.box { @media (…) { } } |
限定在该规则选择器上的媒体查询 | 把断点放在组件旁 |
嵌套保持浅层
两三级通常仍然好读。更深的树会重现 Sass 团队遇到的「选择器金字塔」问题:难 diff、specificity 升高、样式只有塞进一个巨型父级才说得通。优先用清晰的组件根(.card、.nav),嵌套状态和近邻子元素 — 不要嵌整页。
组件打包:tokens + nest + 状态
这一小段把 toolkit 最近三个想法串在一起,而不是重讲 calc 或变量课:
.panel {
--panel-pad: 1rem;
padding: var(--panel-pad);
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
.heading {
margin: 0 0 0.5rem;
font-weight: 600;
}
.actions {
display: flex;
gap: calc(var(--panel-pad) / 2);
margin-block-start: var(--panel-pad);
}
&.is-compact {
--panel-pad: 0.5rem;
}
@media (max-width: 36rem) {
.actions {
flex-direction: column;
}
}
}
Token 仍是自定义属性;calc 仍在推导间距;nesting 只是把 heading、actions、紧凑状态和小屏微调放在同一处。
自己动手
- 把扁平规则
.card {}、.card .title {}、.card .meta {}改写成.card下的 nesting。 - 在
.link上写&:hover,让只有链接(不是嵌套的 span)变色。 - 在
.sidebar里嵌@media (min-width: 40rem)加宽 padding — 在 DevTools 里确认展开后的选择器。 - 把误写的嵌套
:hover { }(没有&)改成&:hover { },并确认它能通过校验。 - 把杂乱的三层 nest 压到两层:把内层块提升成自己的 class。
常见错误
- 嵌套写
:hover { }却不加&— 无效或不是你想要的;用&:hover - 以为裸写
.child等于.parent.child— 那需要&.child - 盲目照搬 Sass 的
&__elementBEM 写法 — 原生 nesting 遵循 CSS 选择器语法 - 嵌五层「因为可以」 — 可读性和 specificity 都会受损
- 以为 nesting 能取代
var()或calc()— 它只是把选择器和 at-rule 分组
小结
- 嵌套子选择器,让组件样式保持就近;默认展开为后代
- 同一元素的状态(
:hover、.is-active)和显式组合器用& - 把
@media嵌在你正在调整的组件旁边 - 嵌套保持浅层;nesting 是结构,不是 token 或数学功能
- Modern Toolkit 下一步:container queries — 按父级尺寸设样式,而不只靠视口
🧠 测试您的知识
测试您的知识
通过这个互动测验挑战自己,看看你对这个主题的理解程度如何
📝 说明
- 仔细阅读每个问题
- 为每个问题选择最佳答案
- 您可以随时重新参加测验
- 您的进度将显示在顶部