SCSS的$z-index Map仅编译期静态替换,无法感知运行时stacking context变化;真正有效的是CSS自定义属性(--z-modal)配合JS动态控制,确保层级决策在运行时生效。

因为 SCSS 的 $z-index Map 只是编译期静态替换,它完全不感知运行时 DOM 结构和层叠上下文(stacking context)变化——你写的 z-index: map-get($z-index, modal),最终输出的只是 z-index: 1000,和手写数字毫无区别。
为什么 DevTools 里看到 z-index 值是对的,但还是被盖住?
常见错误现象包括:
- Modal 容器设置了
opacity: 0.99或transform: translateY(0),触发了新的层叠上下文,子元素的z-index: 950只在 Modal 内部生效,根本比不过外部z-index: 1000的兄弟元素 - Tooltip 用
createPortal挂到下,但body没设position: relative,导致它的z-index和页面其他fixed元素“平级”竞争,而别人用了z-index: 2147483647 - 移动端 Safari 中,父容器只加了
transform: translateZ(0)却没配z-index: 0,子元素的z-index直接被忽略
调试时务必打开 Chrome DevTools → Elements → Computed 面板,搜索 stacking context,逐级确认哪一层父节点标为 Yes。不是值不够大,是“比不了”。
为什么用 @function 计算 z-index 更危险?
比如写 @function get-z-index($key) { @return map-get($z-index, $key) + 10; },看似灵活,实则埋雷:
立即学习“前端免费学习笔记(深入)”;
- DevTools 里只显示最终数字(如
1010),无法反查它来自哪个函数调用或哪条业务逻辑 - JS 完全读不到这个值,后续动态提层(hover 展开、主题切换)只能硬编码覆盖,破坏一致性
- 多人协作时 A 加
+ 10、B 加+ 20,很快突破安全整数范围,且语义彻底丢失
SCSS 不该参与“决策”,只该做“命名锚点”。真正可维护的层级必须由 CSS 自定义属性(--z-modal)承载,并由 JS 在挂载/交互时动态控制。
为什么变量写了却没生效?90% 是作用域断链
典型错误包括:
- 组件文件里写了
z-index: $z-modal,但根本没@use "src/styles/z-index" - 用了
@use "src/styles/z-index" as z,却写成z-index: $z-modal(漏了z.前缀) - 把
$z-modal定义在:root里再用var(--z-modal),混用 Sass 变量与 CSS 自定义属性,导致样式层和逻辑层脱节
正确做法是统一用 @use 显式引入并带命名空间:@use "src/styles/z-index" as z;,然后写 z-index: z.$z-modal-overlay;。同时,在变量文件末尾加一句 :root { --z-modal: #{$z-modal-overlay}; },桥接 JS 动态控制能力。
复杂点始终在于:一旦涉及 Portal、动画、transform 或 opacity,z-index 就不再是线性数值比较,而是树状嵌套结构——这时候再“优雅”的 SCSS Map 也救不了你,必须靠运行时主动识别 stacking context 并干预父容器定位状态。


















