z-index 失效主因是未满足生效前提或配置错误:元素需非 static 定位,且不能处于意外创建的层叠上下文中;tailwind 中 zindex 必须在 theme.zindex 下用对象语法定义,键名加引号、值为字符串,改后需重启服务。

z-index 层级混乱,90% 不是数值没设够,而是没满足生效前提、配置没写对位置、或根本没理解层叠上下文——配了 z-999 却被盖住,大概率是因为元素没定位,或者父容器悄悄创建了独立堆叠上下文。
tailwind.config.js 里 zIndex 必须用对象语法定义在 theme.zIndex 下
Tailwind v3.0+ 已废弃数组写法,也不接受 extend.zIndex 追加。配置错位置或格式,CSS 规则根本不会生成。
-
theme.zIndex必须是完整对象,不能只写extend: { zIndex: { 'modal': '50' } }—— 默认为空,追加无效 - 键名必须加引号:
'modal'✅,modal❌(JS 解析为modal-overlay会被截断) - 值可以是字符串:
'50'✅,但不要写成数字50(虽可能侥幸生效,但不保证兼容性) - 改完必须重启开发服务器——Tailwind 不热重载配置变更
✅ 正确示例:
module.exports = {
theme: {
zIndex: {
'background': '0',
'dropdown': '30',
'tooltip': '40',
'modal-overlay': '60',
'modal-content': '70',
}
}
}
z-index 生效的两个硬条件缺一不可
z-modal-content 写了却没效果?不是数值太小,而是它根本没参与层叠计算。
- 元素自身
position必须是非static值:至少加relative、absolute、fixed或sticky - 该元素不能处于一个被父级意外创建的层叠上下文中:比如父元素有
transform、opacity: 0.99、filter、will-change等属性 - 两个兄弟弹窗各自父容器都触发了新层叠上下文 → 它们的堆叠顺序由 HTML 顺序决定,跟子元素
z-值无关
调试建议:用 Chrome DevTools 的 «Layers» 面板直观查看上下文分组;选中元素后,在 «Computed» 面板确认 position 和 z-index 是否为预期值。
别用 z-[999] 或动态拼接 class
z-[999] 看似灵活,实际是维护黑洞,且生产环境极易漏生成。
- 依赖 JIT 模式扫描
content字段里的字面量字符串;React 中写class={`z-[${depth}]`},构建时根本不会识别 - 没人知道
z-[999]对应什么 UI 角色,下次加个 Tooltip,该用 998 还是 1001?冲突后无法溯源 - 第三方组件(如 Headless UI)可能内联
z-10,而你的z-[999]因注入顺序靠前反被覆盖
✅ 推荐做法:所有层级提前在 theme.zIndex 中明确定义,命名带语义(如 tooltip、modal-overlay),组件内统一用 z-tooltip。
层级冲突常源于缺乏结构化分层意识
零散加 z-50、z-999 不解决问题,反而让冲突更难追溯。真正要建立的是按功能角色划分的数值体系。
- 基础层(0–9):背景、内容容器
- 浮层层(100–199):下拉菜单、Tooltip、Toast
- 模态层(600–799):遮罩
modal-overlay、主体modal-content - 强干预层(2000+):仅限调试或无障碍焦点高亮等特殊场景
- 每个区间留出空隙(比如
dropdown: '130'和tooltip: '150'),方便未来插入中间层
复杂点在于:同一页面多个浮层可能来自不同库(Radix、Headless UI),它们的样式注入顺序、是否带 !important、是否封装在 Portal 中,都会影响最终层级。这时不能只调子元素,得查文档看能否传入 className 覆盖,或用 @layer components 显式重写选择器。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











