必须添加viewport-fit=cover meta标签,否则env(safe-area-inset-top)恒为0;SCSS中需用带constant()降级的mixin封装safe-area-inset,避免@function或@supports误用,且padding不可作用于body。

必须先确保 viewport-fit=cover 生效
没这个 meta,env(safe-area-inset-top) 值永远是 0px,SCSS 再自动化也没用。检查 HTML 中是否包含:
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
注意:viewport-fit=cover 不能被 JS 动态插入,也不能被其他同名 meta 覆盖;微信或 QQ 内置 WebView 若版本过旧(如 iOS 10.x 或 Android 微信 7.0.9 以下),该特性直接不支持,此时 SCSS 生成的 CSS 会被浏览器静默忽略。
用 SCSS mixin 封装 safe-area-inset 四个方向
直接写四遍 padding-top: env(safe-area-inset-top) 太重复,但别用 @function 返回字符串——CSS 函数不能在 SCSS 编译期求值,env() 必须原样输出到最终 CSS 中。
推荐写法是带降级的 mixin:
立即学习“前端免费学习笔记(深入)”;
@mixin safe-area-padding($top: true, $right: false, $bottom: true, $left: false) {
@if $top {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
}
@if $right {
padding-right: constant(safe-area-inset-right);
padding-right: env(safe-area-inset-right);
}
@if $bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
@if $left {
padding-left: constant(safe-area-inset-left);
padding-left: env(safe-area-inset-left);
}
}
调用示例:@include safe-area-padding($top: true, $bottom: true);
关键点:
-
constant()必须写在env()前,否则 iOS 11.1 会整条规则丢弃 - 不要在 mixin 里加
@supports包裹——@supports (padding-top: env(safe-area-inset-top))是运行时检测,SCSS 编译期无法模拟 - 若需逻辑方向适配(如左右 inset 在横屏下互换),应配合
@media (orientation: landscape)单独处理,而非塞进 mixin
避免在 body 上全局应用安全区 padding
很多人习惯给 body 加 padding,但这会破坏滚动行为:Safari 的弹性 overscroll、下拉刷新触发区域偏移、100vh 计算错乱都由此而来。
正确做法是限定作用域:
- 只对最外层内容容器使用,例如
.app-main或.page-content - 固定定位元素(如
position: fixed; bottom: 0)不能靠padding-bottom推开,得用bottom: env(safe-area-inset-bottom)+ 降级 - 若用 Flex 布局撑高容器,
min-height: 100vh比height: 100vh更安全,软键盘弹出时不会塌陷
横屏与 RTL 场景下的逻辑属性 fallback
竖屏时 safe-area-inset-left 和 safe-area-inset-right 基本为 0,但横屏下它们变成关键值——且会随设备方向动态交换。直接写 margin-left: env(safe-area-inset-left) 在 iOS 14.4 及更早版本中大概率失效或不对称。
必须改用逻辑属性:
.header {
margin-inline-start: constant(safe-area-inset-left);
margin-inline-start: env(safe-area-inset-left);
margin-inline-end: constant(safe-area-inset-right);
margin-inline-end: env(safe-area-inset-right);
}
注意:
- 该写法依赖
dir: ltr(HTML 默认即如此),若页面设了dir="rtl",inline-start实际对应右边,需额外判断 - iOS 13.3–14.4 对
margin-inline-*+env()支持不稳定,建议加像素 fallback:margin-inline-start: 20px;置于最前,被后续声明覆盖 - 横屏媒体查询中,可叠加重置:
@media (orientation: landscape) { ... },但不要试图用 SCSS@media嵌套做“条件编译”——环境变量值是运行时决定的
真正麻烦的不是写几行 SCSS,而是记住:所有 env() 都是运行时注入的值,编译期不可知;任何想“预判机型尺寸+硬编码 offset”的思路,在灵动岛、折叠屏、iPad 横屏等场景下都会漏掉边界 case。


















