必须用CSS自定义属性(如--z-modal)管理z-index,因其运行时可读、可继承、可动态更新;Sass变量仅编译期有效,JS无法读取,不支持主题切换、hover提层及Portal场景,导致弹窗被压、Tooltip消失等线上事故。

必须用 --z-modal 这类 CSS 自定义属性统一管理,Sass 变量(如 $z-modal)无法被 JS 读取、不支持运行时更新、主题切换和 Portal 场景下必然失效——线上弹窗被压、Tooltip 消失、暗色模式层级错乱,90% 源于此。
为什么 z-index: var(--z-modal) 写了却没效果?
不是变量没定义,而是链路断在三个关键环节:
-
position仍是static:z-index 对非定位元素完全无效;必须显式设为relative、absolute、fixed或sticky - 变量作用域失效:
:root { --z-modal: 1000; }必须写在全局非scoped样式中(如 Vue 的App.vue根样式、React 的index.css),<style scoped></style>里写:root是无效的 - 拼写或路径错误:定义的是
--z-modal-overlay,使用时写成var(--z-modal)或var(--z_modal_overlay)(中划线 vs 下划线);或未通过@import/<link>加载变量文件
怎样设计不冲突、可扩展的 --z-* 变量?
核心是“留空档、分职责、禁魔法值”,不是按顺序填数字:
- 基础层:
--z-background: -10(用于 canvas 底层、SVG mask) - 内容层:
--z-card: 0、--z-table-row: 1(默认文档流,无需定位) - 浮层层:
--z-tooltip: 100、--z-dropdown: 200(中间空出 99,方便插--z-combo-box: 150) - 强弹窗层:
--z-modal-overlay: 1000、--z-modal-content: 1010(overlay 和 content 差值明确,避免遮罩透出) - 绝对禁止:
--z-max、--z-top、--z-9999——语义缺失,且与 Bootstrap(z-index: 1070)、Headless UI 等库默认值直接冲突
Portal/Teleport 场景下为何仍被压住?
把 Modal 挂到 下 ≠ 自动置顶—— 默认无 position,不构成 stacking context,它的子元素仍和其他绝对定位元素在同一全局上下文中竞争。
立即学习“前端免费学习笔记(深入)”;
- 检查是否其他地方用了
z-index: 2147483647这类极端值,直接碾压你的--z-modal: 1000 - 确保
至少有position: relative(哪怕只是占位),否则它不参与层叠排序基准 - 更稳妥做法:把 Portal 内容挂到一个显式带
position: fixed+z-index的 wrapper 下,而非裸挂 - 注意 transform、opacity ,它的父级若带
transform: scale(1),整个子树就被锁死在局部层级里
最常被忽略的一点:CSS 变量本身不参与层叠计算,它只是个字符串容器。你写了 --z-modal: 1000,但没在任何选择器里写 z-index: var(--z-modal),那它就只是个没用的声明。变量定义和实际使用必须成对出现,缺一不可。


















