position: fixed + display: grid 是实现视口底部固定底栏的正确组合:fixed 锁定位置,grid 排列子项;需设 bottom: 0、left: 0、right: 0、z-index,用 height: 60px + env(safe-area-inset-bottom) 适配安全区,并为 body 预留 padding-bottom。

用 position: fixed + display: grid 组合最直接
单纯靠 Grid 无法让底栏“固定在视口底部”,必须搭配 position: fixed。Grid 只负责内部导航项的排列,不是定位机制。很多人误以为 grid-template-rows: auto 1fr auto 能实现固定效果,那只是“粘底”(内容少时到底部,内容多时被顶下去),不是真正固定。
正确做法是:外层容器用 position: fixed 锁定位置,再用 Grid 布局子元素。这样既保证视觉固定,又保留 Grid 的响应式对齐能力。
-
bottom: 0、left: 0、right: 0缺一不可,否则在部分 Android 浏览器里会偏移 - 必须设
z-index,尤其当页面有 modal 或 toast 时,否则被遮挡 - 避免用
height: 100vh作为底栏高度——iOS Safari 的vh在地址栏收起/展开时会跳变 - 推荐用固定像素高度(如
height: 60px)+env(safe-area-inset-bottom)补偿安全区
env(safe-area-inset-bottom) 不是可选项,是必填项
iOS 和部分 Android 设备(带虚拟导航键)会在屏幕底部留出不可交互区域。只加 padding-bottom: env(safe-area-inset-bottom) 会导致底栏整体上移,必须同步抵消。
标准写法是:
footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 60px;
padding-bottom: env(safe-area-inset-bottom);
margin-bottom: calc(-1 * env(safe-area-inset-bottom));
}
注意:@supports 包裹不是为了“优雅降级”,而是防止不支持 env() 的旧浏览器解析失败后整段样式失效。应写成:
@supports (padding-bottom: env(safe-area-inset-bottom)) {
footer {
padding-bottom: env(safe-area-inset-bottom);
margin-bottom: calc(-1 * env(safe-area-inset-bottom));
}
}
Grid 子项用 repeat(auto-fit, minmax()) 实现等宽自适应
移动端底栏通常有 3–5 个图标+文字项,既要等宽,又要随屏幕缩放自动增减列数(比如小屏显示 3 个,大屏显示 5 个),repeat(auto-fit, minmax(80px, 1fr)) 是目前最稳的写法。
关键点:
-
minmax(80px, 1fr)中的80px是最小宽度,确保图标+文字不被压缩变形;1fr表示剩余空间均分 - 用
auto-fit(不是auto-fill),它会合并空轨道,避免空白格撑开布局 - 必须配
gap,否则 iOS Safari 里相邻项点击热区可能重叠 - 别忘了给每个
a设置display: flex+flex-direction: column,不然文字不会垂直居中
示例:
footer nav {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
gap: 4px;
padding: 8px 12px;
}
body 必须预留底栏高度,否则内容被遮挡
这是最容易被忽略的一步。仅靠 position: fixed 不会把文档流里的内容“推开”,所有滚动内容都会从底栏下方穿过。
解决方式只有两个有效选择:
- 给
body加padding-bottom: 60px(数值等于底栏高度),再加padding-bottom: env(safe-area-inset-bottom)适配刘海屏 - 或改用
margin-bottom,但需确保父容器没overflow: hidden,否则滚动被截断
不要试图用 transform: translateY() 或绝对定位去“推”内容——它们破坏滚动锚点,且在 iOS 微信内置浏览器中极易触发渲染异常。
复杂点在于:当键盘弹出时,env(safe-area-inset-bottom) 值会突变,但 padding-bottom 不会自动重算。如果页面含输入框,必须监听 focusin/focusout 事件动态调整 body 的 padding-bottom。这点几乎没人提,但线上真实崩溃多源于此。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











