z-index变量必须在:root中声明才全局有效;组件内写:root无效,UniApp需在App.vue非scoped style顶部统一定义,否则uni-modal等原生组件读不到;变量仅管理数值,无法突破父容器创建的层叠上下文限制。

z-index 变量必须配合 :root 声明才真正全局可用
直接在某个组件里写 :root { --z-toast: 2000; } 是无效的;CSS 变量要跨组件生效,必须统一注入到根作用域。UniApp 中推荐在 App.vue 的 <style> 块顶部声明,且确保它不被 scoped 限制:
:root {
--z-base: 0;
--z-header: 100;
--z-sidebar: 200;
--z-modal: 1000;
--z-toast: 1100;
--z-loading: 1200;
--z-fullscreen-guide: 1500;
}如果用了 scoped,变量只在当前组件内有效,uni-modal 这类原生组件根本读不到——它们渲染在 body 下,不属任何 scoped 范围。
常见错误现象:uni.showToast 仍被遮挡,检查发现变量只定义在某个 page 的 style 里,没上浮到 :root。
uni-modal 和 uni-toast 的 class 名不暴露,得靠属性选择器或自定义 class 注入
UniApp 的 uni-modal 和 uni-toast 渲染后 DOM 是动态生成的,class 名带哈希或无固定名,不能直接写 .uni-modal { z-index: var(--z-modal); }。必须换路径:
立即学习“前端免费学习笔记(深入)”;
- 方式一(推荐):在调用
uni.showModal时传入customStyle,显式加z-index:uni.showModal({ title: '提示', content: '确定删除?', customStyle: 'z-index: var(--z-modal); position: relative;' }); - 方式二:用属性选择器匹配原生组件的内部结构,例如 toast 通常带
data-type="toast"属性:[data-type="toast"] { z-index: var(--z-toast) !important; position: relative; }注意加!important—— 因为 uni-app 内部样式有高优先级,不加可能被覆盖。
响应式场景下 z-index 变量需配合媒体查询重定义
移动端弹窗和桌面端侧边栏的层级关系经常冲突。比如小屏下 --z-sidebar 设为 200 没问题,但大屏展开时它变成固定定位,若值仍低于 --z-modal,就会盖不住模态框;反之,设太高又可能压住系统级通知。
解决方案是用媒体查询分层重定义变量:
@media (min-width: 768px) {
:root {
--z-sidebar: 900;
}
}
@media (max-width: 767px) {
:root {
--z-sidebar: 200;
--z-modal: 1050; /* 小屏模态框需要更高一点 */
}
}注意:变量重定义必须在 :root 下,不能写成 .sidebar { --z-sidebar: 900; } —— 那样只在该元素作用域生效,无法被其他组件读取。
父容器触发层叠上下文会锁死子元素的 z-index 变量效果
即使你把 --z-modal 设成 9999,如果模态框被包在一个 opacity: 0.99 或 transform: translateZ(0) 的容器里,它的整个子树就困在那个层叠上下文中,再高的值也出不去。
排查要点:
- 打开 Chrome DevTools → 「Layers」面板,看模态框是否被框进一个独立合成层
- 检查模态框所有祖先节点,是否有以下任一属性:
opacity < 1、transform != none、filter != none、will-change - 真需要模糊/动效?把
filter: blur(2px)移到背景蒙层上,而不是整个 modal 容器
变量只是让数值管理更清晰,但解决不了层叠上下文这个底层约束——这是最容易被忽略的硬性前提。


















