z-index: var(--z-modal)无效,90%因元素position为static;必须显式设relative/absolute/fixed/sticky,且变量须在顶层非scoped样式中定义才全局有效。

z-index: var(--z-modal) 无效?先看 position 是不是 static
90% 的 z-index: var(--z-modal) 不生效,根本原因不是变量没定义,而是目标元素的 position 仍是 static。浏览器直接忽略该 z-index,Computed 面板里会显示 auto 或灰掉。
必须显式设置为以下之一:relative、absolute、fixed 或 sticky。常见误操作包括:
- 只给父容器设
position: relative,子元素没设定位,z-index白写 - 用
transform: translateZ(0)触发硬件加速,却意外创建新层叠上下文,把子元素“关”在里面 - 第三方组件(如
uni-modal)挂载到下,但没设position,导致失去统一调度基准
:root 定义 --z-modal ≠ 全局可用
在某个 <style scoped></style> 里写 :root { --z-modal: 1000; } 是无效的——scoped 会加属性选择器,:root 被锁死在当前组件作用域内,其他地方读不到。
真正全局生效的写法只有一处:项目顶层非 scoped 的 <style></style> 块中(如 Vue 的 App.vue、React 的 index.css),直接写:
立即学习“前端免费学习笔记(深入)”;
:root {
--z-base: 0;
--z-toast: 800;
--z-dropdown: 900;
--z-modal: 1000;
--z-fullscreen: 1500;
}
UniApp 等跨端框架尤其要注意:uni.showToast 渲染的 DOM 在 下,如果变量只定义在某个 page 文件里,它完全看不到。
多个组件共用变量时,如何避免冲突和覆盖
把所有层级都塞进 :root 容易失控——一个弹窗组件升级了 --z-modal,结果侧边栏菜单也被顶到前面。
更稳妥的方式是按功能域划分作用域,让变量就近定义:
- 模态框内部写:
.modal { --z-element: var(--z-modal, 1000); z-index: var(--z-element); } - 下拉菜单用
--z-dropdown,Toast 用--z-toast,彼此隔离 - 数值至少留 10 点间隙:
--z-tooltip: 300、--z-modal: 900,中间预留插入空间 - 禁用
calc(var(--z-base) + 10)—— 旧版 Safari 对calc+var()支持不稳定
JS 动态读写 CSS 变量时,字符串和数值别混用
通过 JS 修改层级很常见(比如主题切换时动态降级弹窗),但 getComputedStyle().getPropertyValue('--z-modal') 返回的是字符串,直接比较会出错:
- 要数值比较,必须先
parseInt():parseInt(getComputedStyle(el).getPropertyValue('--z-modal')) - 用
element.style.setProperty('--z-modal', '1200')动态提层时,传入值必须是字符串 - 若需 JS 控制 Portal 类组件(如 Modal/Tooltip)的层级,应在挂载后检查并设置父容器 stacking context:
document.body.style.position = 'relative'(若未设)
复杂点在于:一旦用了 Portal 或动画触发新 stacking context,z-index 就不再是线性比较,而是树状嵌套——这时候变量本身只是起点,关键在运行时是否同步更新了实际参与比较的层叠上下文。


















