safari中z-index“穿透”实为3d区域按真实z轴排序,绕过层叠上下文;首要原因是transform-style: preserve-3d父链中断,需逐级显式声明并加-webkit-前缀,backface-visibility:hidden必须写在翻转元素自身且前置非none transform,perspective须设于独立容器并带-webkit-前缀。

z-index 在 Safari 的 3D 变换中“穿透”,不是 z-index 失效,而是它压根没参与你预期的层级比较——Safari 把整个 3D 区域当作一个独立渲染单元,按真实 Z 轴坐标排序,完全绕过层叠上下文规则。
transform-style: preserve-3d 父链中断是首要原因
Safari 要求从 .scene 到翻转元素之间每一层父容器都必须显式声明 transform-style: preserve-3d。漏掉任意一层(比如中间有个 .card-wrapper 没设),子元素的 translateZ() 就被归零,所有面塌陷到同一平面,Z 轴排序退化为 HTML 文档流顺序。
- 用 Safari 开发者工具选中穿模元素 → Styles 面板检查
transform-style是否被 strike-through(被覆盖) - 常见干扰源:
overflow: hidden、filter、opacity: 0.99的父容器,会意外创建新 stacking context 并中断 3D 链 - 某些 UI 框架或 normalize.css 会重置为
transform-style: flat,需手动覆盖
backface-visibility: hidden 必须写在翻转元素自身上
这个属性不是可选项,是 Safari 启用深度缓冲(depth buffer)的硬性开关。它不继承,也不接受“父设子用”。
- 错误写法:
.card { backface-visibility: hidden; }—— 容器设了,但.front和.back依然无效 - 正确写法:
.card .front, .card .back { backface-visibility: hidden; } - 伪元素也要单独加:
.card::before { transform: rotateY(180deg); backface-visibility: hidden; } - 关键前提:
backface-visibility前必须有非none的transform声明,例如transform: rotateY(0)
perspective 不能挂在 body 上,且必须带 -webkit- 前缀
把 perspective 加在 或 上,会让整个页面变成一个巨型 3D 上下文。此时 position: fixed 元素会失去视口锚定能力,滚动时错位、消失,视觉上就像“穿透”遮罩层。
- 正确做法:用独立容器包裹 3D 区域,例如
<div class="scene"></div>,然后只在它上面写:.scene { perspective: 1000px; -webkit-perspective: 1000px; transform-style: preserve-3d; } - iOS Safari 15.4 之前版本(包括当前主流 iOS 16.x)不识别无前缀的
perspective,必须加-webkit-perspective - 避免在
.scene外层再套带transform或opacity 的容器,否则会提前创建 stacking context
overflow: hidden 是最简单有效的兜底方案
当无法逐层修复 3D 链时,给任意一级父容器(非 )加 overflow: hidden,可强制 Safari 回退到传统层叠模型,让 z-index 重新生效。
- 适用场景:复杂嵌套、第三方组件不可改、时间紧急上线
- 副作用:可能裁剪内容或影响滚动表现,需实测
- 替代方案:给被遮挡的高优先级元素(如弹窗)加
transform: translateZ(999px),把它“推”到 3D 场景最前方 - 注意:
translateZ数值不是越大越好,iOS Safari 对超大值解析不稳定,建议控制在100px–500px区间
真正难的不是调哪个数值,而是得同时盯住三层:DOM 层级是否连续、CSS 属性是否写对位置、WebKit 渲染引擎是否认得那个前缀——少一个,z-index 就在 Safari 里彻底“隐身”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











