底部安全区适配的粘性布局需 viewport-fit=cover + min-height:100dvh + flex:1主内容撑开 + body padding-bottom:env(safe-area-inset-bottom) + footer自然高度,禁用fixed/sticky及固定高度。

在 H5 移动端实现“底部安全区适配的粘性布局”,核心不是让 footer 粘在视口底部,而是让它**稳稳停在安全区(safe area)内,同时保持粘性行为**:内容少时沉底,内容多时自然跟随、不遮挡、不被截断。这需要 Flex 布局 + 安全区感知 + 视口适配三者协同,不能只靠 flex: 1 一招打天下。
必须开启 viewport-fit=cover
这是全面屏适配的前提。没有它,iOS Safari 默认把页面限制在安全区内,100vh 实际等于“安全区高度”,导致 Flex 容器高度不足,footer 依然悬空:
✅ 正确写法:<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
❌ 错误写法:漏掉 viewport-fit=cover,或只写 contain —— 后者会让页面缩进,Flex 的 100vh 失效,粘性逻辑直接崩盘。
用 min-height: 100dvh 替代 100vh(推荐)
iOS Safari 中 100vh 在地址栏展开/收起时会跳变,且不区分安全区;而 100dvh(dynamic viewport height)是 CSS 新标准,能动态响应安全区变化,更精准反映可用视口高度:
- 支持情况:Safari 16.4+、Chrome 108+、Firefox 110+,H5 主流机型基本覆盖
- 降级方案:不支持时回退到
100vh,用@supports判断 - 写法示例:
.wrapper {<br> min-height: 100dvh;<br> min-height: 100vh; /* fallback */<br> display: flex;<br> flex-direction: column;<br> }
主内容区 flex: 1 + 底部留白 padding
footer 要待在安全区内,不能靠自己定位,而应由主内容区“撑开”并预留底部空间:
-
main { flex: 1; }—— 吃掉 header 之后、footer 之前的所有剩余空间,把 footer “顶”到最下 -
body { padding-bottom: env(safe-area-inset-bottom); }—— 让 body 底部留出安全区间隙,避免内容被横条遮挡 -
footer { padding: 12px env(safe-area-inset-left) 12px env(safe-area-inset-right); }—— 两侧也用env()避开左右异形区域(如曲面屏)
footer 本身不设高度,靠内容自然撑开
固定 height 容易和安全区冲突;文字贴边难读;动态内容(如多语言版权信息)高度不可控:
- ✅ 保留默认
flex: none(即不写 flex 相关属性) - ✅ 可加
margin-top: auto作为双保险,在复杂嵌套中比单靠flex: 1更鲁棒 - ❌ 不要写
height: 60px或flex: 1—— 会拉伸、盖内容、溢出视口 - ❌ 不要用
position: fixed或sticky—— 它们无视安全区计算,且破坏文档流可访问性











