z-index必须按功能域分组并语义化命名,如--z-toast、--z-modal等,数值间留至少10点空档;变量须在顶层非scoped样式中定义于:root才全局生效,且所有使用处必须配合relative/absolute/fixed/sticky定位。

z-index数值必须按功能域分组,不能堆成一串整数
把所有层级塞进 :root 并用 --z-1、--z-2 这类命名,短期省事,长期必乱。不同模块(如弹窗、下拉菜单、Toast)共用同一变量,一旦某处升级数值,其他地方就可能意外浮起或沉底。
实操建议:
- 按功能定义语义化变量:
--z-toast、--z-dropdown、--z-modal、--z-fullscreen - 数值之间留至少 10 点空档:比如
--z-tooltip: 300、--z-dropdown: 900、--z-modal: 1000,中间预留调试和插槽空间 - 基础层用
--z-base: 0,避免硬写z-index: 0—— 后期统一降级时只需改一处
避免在 scoped 样式中定义 :root 变量
:root { --z-modal: 1000; } 写在 Vue 的 <style scoped></style> 或 React 组件内 CSS 模块里,变量根本不会全局生效。scoped 会为 :root 加属性选择器,导致它被锁死在当前组件作用域内,其他文件读不到。
真正能被全项目读取的写法只有一处:顶层非 scoped 的样式入口,比如:
:root {
--z-base: 0;
--z-toast: 800;
--z-dropdown: 900;
--z-modal: 1000;
--z-fullscreen: 1500;
}
UniApp、Taro 等跨端框架尤其要注意:uni.showToast 渲染的 DOM 挂在 下,如果变量只定义在某个 page 文件里,它完全看不到。
不要在 calc() 里嵌套 var() 做动态计算
z-index: calc(var(--z-base) + 10) 看似灵活,但旧版 Safari(iOS 15.4 及更早)对 calc() + var() 的支持不稳定,容易回退为 auto,导致层级失效。
更稳妥的做法是直接定义完整变量:
- ✅ 推荐:
--z-toast-high: 850、--z-modal-low: 950 - ❌ 避免:
z-index: calc(var(--z-modal) - 50)
JS 动态读写时也要注意:getComputedStyle(el).getPropertyValue('--z-modal') 返回的是字符串,比较前得先 parseInt(),否则 '1000' > '999' 会是 false。
z-index 工具类必须绑定 position 才有意义
单独写一个 .z-1000 { z-index: 1000; } 类,却不配 position: relative,等于没写。CSS 规范明确要求:z-index 只对定位元素生效。
实操建议:
- 工具类命名隐含定位意图,比如
.relative-z-1000或组合使用:class="relative z-1000" - 用 CSS 自定义属性统一基数,后期全局调整只需改
:root里的值,所有z-index: var(--z-modal)自动响应 - 禁止用
z-9999这类魔法数字——它掩盖了结构问题,也容易在嵌套中被父级 stacking context 截断
最常被忽略的一点:z-index 数值大小本身不决定“谁在最上”,只决定“在同一层叠上下文中谁更靠前”。跨上下文时,起决定作用的是父级的绘制顺序和是否创建了新 stacking context —— 比如 transform: translateZ(0) 或 opacity: 0.99 会悄悄关住子元素。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











