@at-root 是显式重置输出层级的指令,仅提级直接包裹的规则,不自动跳出嵌套块或 at-rule;错误嵌套、滥用 &、插值不当及误作 scoped 穿透均导致失效。

@at-root 不是“退出嵌套”的快捷键,而是显式重置输出层级的指令;用错位置或参数,它根本不会跳出你想要的地方。
为什么 .modal { .is-loading { @at-root .is-loading { } } } 仍编译成 .modal .is-loading
常见错误现象:@at-root 必须直接作用于要提级的选择器,不能套在另一个嵌套块里再用。Sass 把它当普通嵌套处理,根本没触发“提级”逻辑。
- 错误写法:
.modal { .is-loading { @at-root .is-loading { opacity: 0.5; } } }→ 编译后仍是.modal .is-loading - 正确写法:
.modal { @at-root .is-loading { opacity: 0.5; } }或更干净地提至文件顶层 -
@at-root只影响它**直接包裹的规则**,不擦除父级上下文;嵌套越深,越容易误以为“写在里面就能跳出来”
@at-root (without: all) 才能真正脱离 @media 和组件作用域
默认 @at-root 只跳出选择器嵌套,对 @media、@supports、@layer 等 at-rule 是“穿透”的——也就是说,你在媒体查询里写 @at-root .theme-dark,生成的 CSS 仍被包在该 @media 块内,导致小屏下无法生效。
-
@at-root (without: media):跳出媒体查询,但还卡在父选择器里(如.card .theme-dark) -
@at-root (without: rule):等价于裸@at-root,只提选择器,保留所有 at-rule 包裹 -
@at-root (without: all):彻底跳出所有上下文,包括@media、@supports、@layer,生成真正全局可用的类
& 在 @at-root 内部无效,插值也受作用域限制
& 依赖当前嵌套上下文,而 @at-root 已经把它清除了。混用时会报错或产出意外结果。
- ❌ 错误:
.card { @at-root &__header { } }→ 编译报错,“& is not allowed in @at-root” - ❌ 危险:
@each $type in (primary, secondary) { @at-root .btn-#{$type} { } }→ 插值可工作,但若改用@let $type: ...(Sass 1.50+),变量在@at-root内不可见 - ✅ 安全:
@at-root .btn-#{$type} { color: map-get($colors, $type); },前提是$type是@each的循环变量,且#{$type}不在选择器开头
别把 @at-root 当成 Vue scoped CSS 穿透方案
@at-root 控制的是 Sass 编译时的选择器生成位置,不解决运行时 Shadow DOM 或 scoped CSS 的样式隔离问题。
- ❌ 误用:
.parent { @at-root .child { } }替代 Vue 的:deep()→ 生成真实全局类名,可能污染其他组件 - ⚠️ 注意:
@at-root后生成的类没有作用域保护,和手写顶层类名效果一致,仅适合明确需要全局语义的工具类(如.is-loading、.sr-only) - 真正需要穿透时,应使用框架提供的机制(如 Vue 的
:deep()、:slotted()),而非靠 Sass 提级“绕过”
最常被忽略的一点:@at-root 的行为高度依赖它出现的位置和上下文——它不看你的意图,只按 Sass 解析规则执行。写之前先问一句:这个规则,我到底想让它出现在最终 CSS 的哪一层?
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











