safe-area-inset-* 是 ios 11+ 等支持的 css 环境变量,用于获取刘海/挖孔/指示条区域尺寸,但仅在 viewport-fit=cover 启用时生效,且需配合 @supports 检测与默认值回退,android 厂商支持极不统一。

什么是 safe-area-inset-*,它为什么不是所有设备都生效
安全区变量 env(safe-area-inset-top) 等是 iOS 11+ 和部分 Android(如三星、华为新机型)WebView 支持的 CSS 环境变量,用来获取刘海/挖孔/底部 Home 指示条占用的空间。但它不是 CSS 标准属性,不支持就直接被忽略——所以加了没报错,但页面照样溢出,这是最常被误以为“适配失败”的原因。
关键判断:只在启用了视口 viewport-fit=cover 时,env() 才可能有值;否则 env(safe-area-inset-bottom) 返回 0 或无效值。
- 必须在
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">中声明viewport-fit=cover - Android 厂商支持极不统一:Pixel 系列基本支持,小米/OPPO 多数 WebView 不识别
env(),不能依赖 - 桌面 Safari(macOS)也支持该变量,但仅当页面全屏(PWA)且有虚拟刘海(如外接带摄像头的显示器)时才非零
padding + env() 组合写法最容易漏掉回退机制
很多人直接写 padding-top: env(safe-area-inset-top);,结果在不支持的安卓机上顶部塌陷——因为没设默认值。CSS 的 env() 不支持 fallback 语法(如 env(safe-area-inset-top, 20px) 是非法的),必须靠层叠覆盖。
正确做法是先设一个保守默认值,再用支持查询覆盖:
立即学习“前端免费学习笔记(深入)”;
header {
padding-top: 20px;
}
@supports (padding-top: env(safe-area-inset-top)) {
header {
padding-top: env(safe-area-inset-top);
}
}-
@supports检测的是整个声明是否被支持,不是单个函数,所以必须写成padding-top: env(...)形式 - 别用
calc(10px + env(safe-area-inset-top))——旧版 Safari 对 calc + env 的解析不稳定,iOS 14.5 之前容易失效 - 如果用 CSS-in-JS(如 styled-components),注意服务端渲染时
env()无法计算,需确保 SSR 输出的默认值合理
fixed 定位元素在刘海区下方被遮挡怎么推上去
底部 position: fixed 的 TabBar 或操作栏,常被 iPhone 底部指示条挡住。单纯加 padding-bottom 不行——因为 fixed 元素脱离文档流,padding 不影响它。
真正有效的方案是改用 margin-bottom 或 inset(推荐后者,更语义化):
.tabbar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
/* 错误:padding-bottom 不起作用 */
/* 正确:用 inset-bottom 控制距离底边距离 */
inset: auto 0 env(safe-area-inset-bottom) 0;
}-
inset是top/right/bottom/left的简写,现代 Safari 支持良好;老版本可降级为bottom: env(safe-area-inset-bottom) - 避免用
height: calc(100vh - env(safe-area-inset-bottom))做全屏布局——iOS Safari 的100vh在地址栏收起/展开时会跳变,导致内容抖动 - 微信内置浏览器(X5 内核)完全不支持
env(),需 JS 检测 UA +window.innerHeight与屏幕高度差来模拟
JS 读不到 safe-area-inset,只能间接判断
CSS 变量无法通过 getComputedStyle 直接读取,getComputedStyle(el).getPropertyValue('env(safe-area-inset-bottom)') 返回空字符串。想在 JS 里响应安全区变化,只能曲线救国。
- 监听
window.visualViewport的resize事件(iOS Safari 支持),对比visualViewport.height和window.screen.height差值,估算底部安全距离 - 用
matchMedia('(prefers-reduced-motion)')这类媒体查询做代理检测不靠谱,但matchMedia('(max-height: 800px) and (orientation: portrait)')可粗筛 iPhone 尺寸,作为兜底条件 - 真要精确控制(比如 Canvas 渲染区域避让),建议只在 iOS 上启用安全区逻辑,Android 统一按 0/0/0/0 处理——省事且无明显体验损失
安全区适配不是“加几个 env 就完事”,而是围绕 viewport-fit、CSS 层叠、fixed 元素行为、JS 能力边界这四点反复验证。最常被忽略的是:没有 viewport-fit=cover,后面所有 env() 都是摆设。










