viewport-fit=cover是env()生效的硬性前提,缺失则所有安全区变量恒为0px;必须与width=device-width、initial-scale=1.0共存于同一meta标签中,且不可被js或第三方库覆盖。

viewport-fit=cover 必须加,否则 env() 全是 0px
不加这个 meta,env(safe-area-inset-top) 在 iOS 上永远返回 0px,不是“没生效”,而是系统压根不注入这些变量。常见错误是只写了 width=device-width, initial-scale=1.0,漏掉 viewport-fit=cover。
正确写法只有一种:<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">。注意:不能被 JS 动态覆盖,不能被第三方 UI 库(如某些 PWA 插件)悄悄重写。
Android Chrome(非 WebView)即使加了这行也无效——env() 是 WebKit 特性,别指望它跨引擎工作。
@supports (padding-top: env(safe-area-inset-top)) 是硬性要求
直接写 padding-top: env(safe-area-inset-top) 在 iOS 11.1 及更早版本会整条规则失效(不是取 0,而是被浏览器丢弃),导致顶部导航栏突然紧贴状态栏被裁切。
必须用 @supports 包裹,且语法必须严格:
- ✅ 正确:
@supports (padding-top: env(safe-area-inset-top)) { ... } - ❌ 错误:
@supports (env(safe-area-inset-top)) { ... }(语法非法,整个块被忽略) - ❌ 错误:
@supports (safe-area-inset-top) { ... }(不是 CSS 属性,不识别)
fallback 值建议设为 20px 或 44px,别用 0——老设备上 0 会让内容顶到屏幕最顶,被系统 UI 截断。
fixed 元素不能靠 padding-top 适配
对 position: fixed; top: 0; 的导航栏,加 padding-top: env(safe-area-inset-top) 完全无效。padding 是内部空间,fixed 元素贴的是视口顶部,不是父容器内边距。
真正起作用的是调整定位偏移:
- 用
top: env(safe-area-inset-top, 0px)替代top: 0 - 顺序必须是:
top: constant(safe-area-inset-top);→top: env(safe-area-inset-top);→top: 0;(但constant()已被 Safari 15+ 彻底废弃,现代项目可删) - 如果用了
inset(如inset: auto 0 env(safe-area-inset-bottom) 0),它比单独设top/bottom更稳妥
注意:env() 不能出现在 React/Vue 的 style={{ top: 'env(safe-area-inset-top)' }} 里——JS 不解析 CSS 函数,只会当字符串透传,浏览器收不到。
底部手势条比顶部刘海更易出问题
env(safe-area-inset-bottom) 值在 iPhone 竖屏下通常是 34px,但它直接影响 position: fixed; bottom: 0; 的 tabbar。只给父容器加 padding-bottom 没用,必须作用于 fixed 元素自身。
常见翻车点:
- 软键盘弹出时,
env(safe-area-inset-bottom)不更新——值还是 34px,但视口高度已变,结果按钮被顶到屏幕中央 - 横屏时
env(safe-area-inset-left)和env(safe-area-inset-right)可能非零(尤其 iPad),但多数 H5 不处理,留白或溢出 - 不要写
height: calc(100vh - env(safe-area-inset-bottom))——100vh在滚动/键盘弹出时会动态变化,计算结果不可靠
真机测试不能跳过。iOS 12.x 的 fixed 头部在某些 WebView 中仍会异常,仅靠模拟器看不出。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











