scss map 管理 z-index 本质是编译期硬编码,输出静态值(如 z-index: 1000),无法应对运行时 stacking context 变化;真正可控的是 sass 定义语义锚点 + css 自定义属性供 js 动态接管。

SCSS 里用 Map 或变量管理 z-index,本质是编译期硬编码,解决不了运行时 stacking context 变化带来的层级断裂;真正可控的做法是:用 Sass 变量做语义锚点 + CSS 自定义属性暴露给 JS 动态接管。
为什么 SCSS $z-index-map 实际上没用
它输出的只是静态数字,比如 map-get($z-index, modal) 编译后就是 z-index: 1000,和手写没区别。一旦父容器因 opacity: 0.99、transform 或 filter 创建了新 stacking context,子元素再高的 z-index 也出不去这个“盒子”。
常见错误现象:
- 下拉菜单嵌在 Modal 内,
z-index: 950被 Modal 容器截断,完全不可见 - 用
createPortal把 Tooltip 挂到body下,但body没设position: relative,导致它的z-index和页面其他fixed元素平级比大小,而别人用了z-index: 2147483647
怎么用 Sass 变量做最小可行管理
只做一件事:定义语义清晰、留间隙、可校验的命名常量,不参与计算,不生成 CSS 规则。
- 值必须为整数,严格递增且留足空档:
$z-tooltip: 700、$z-dropdown: 900、$z-modal-overlay: 1000,中间空出 99 方便插入新层 - 按职责分区,不跨区越界:
$z-background: -10(压底图)、$z-toast: 990(浮层)、$z-modal-content: 1010(弹窗内容) - 禁用
$z-max、$z-9999类别名——无语义、易冲突、破坏可读性 - 必须
@use引入,调用时带命名空间:z-index: z.$z-modal-overlay,否则编译后仍是原样字符串
为什么必须导出为 CSS 自定义属性
Sass 变量编译完就消失了,JS 无法读取。Modal hover 展开下拉、Portal 挂载后动态提层、动画中防闪烁——这些都依赖运行时控制。
- 在
:root或模块作用域内导出::root { --z-modal: #{$z-modal-overlay}; } - 组件内直接使用:
z-index: var(--z-modal); - JS 动态调整:
element.style.setProperty('--z-modal', '1050'),确保与视觉表现一致 - 禁止混用:
z-index: $z-modal和z-index: var(--z-modal)同时存在,会造成维护断层
Portal 类组件必须手动接管 stacking context
Modal、Tooltip 这类挂到 body 的组件,默认 body 不是定位元素,不构成 stacking context。它们的子元素会和全站所有定位元素“平级”比大小,极易被第三方库覆盖。
- 挂载后检查并设置:
document.body.style.position = 'relative'(若未设) - 多个 Portal 共用一个挂载点时,需 JS 动态递增
--z-modal,避免后开的被先开的盖住 - 动画开始前提升
z-index,否则transform触发新 stacking context,过渡过程会出现层级闪烁 - 推荐做法:给 Portal 挂载点加一层 wrapper,并设
position: relative; z-index: 1055(参考 Bootstrap)
复杂点在于:一旦用了 Portal、transform、opacity 或响应式媒体查询里隐式创建 stacking context,z-index 就不再是线性比较,而是树状嵌套——这时候再“优雅”的 SCSS Map 也救不了你。真正要盯住的,永远是 DevTools 的 Layers 面板和 Computed 中的 stacking context 标记。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











