Appearance
很多业务页本质都是同一类问题:固定高度的卡片里,上下几块内容怎么分高度——顶部信息、标题要完整展示,中间摘要尽量多吃剩余空间,底部审核区与主按钮永远留在视区内,中间过长则自己滚动、不把下面挤没。
前端布局有点像一套小型物理规则:不是「会背属性就自然会排」,默认行为里常有反直觉处——例如 flex-shrink 默认是 1,子项在主轴空间不够时会先被允许压扁,于是审核按钮区可能被挤没。下面从默认值说到审核页常用写法。
核心模型
css
.parent {
height: 100%;
display: flex;
flex-direction: column;
}
.auto {
flex: 0 0 auto; /* 与「只写 flex-shrink: 0」在本场景等价,见下节 */
}
.fill {
flex: 1 1 0;
min-height: 0;
}Flex 子项默认是 flex: 0 1 auto(为什么要单独管 flex-shrink)
父级写了 display: flex 后,子项自己不写 flex 时,浏览器默认相当于:
css
.child {
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
}也就是 flex: 0 1 auto:
flex-grow: 0:有剩余空间时,默认不主动变大。flex-shrink: 1:空间不够时,默认允许被压缩——纵向卡片里空间吃紧时,浏览器就可能去压「审核区」「底栏」,标签和按钮被挤扁或看不全,常来自这里。flex-basis: auto:先按内容尺寸或你写的width/height参与基础占位。
容易记混的一点:子项的 flex-grow 默认确实是 0——所以默认情况下子项不会主动抢剩余空间;但日常里很多人写的 flex: 1 会把 flex-grow 设成 1,和「什么都不写」不是一回事。
若你主动写:
css
.child {
flex: 1;
}在常见浏览器里会按 flex 简写规则解析为 flex-grow: 1、flex-shrink: 1、flex-basis: 0%,即:
css
.child {
flex: 1 1 0%;
}(规范里 0 与 0% 在 flex-basis 上多数场景表现一致;写 flex: 1 1 0 与 flex: 1 在「吃剩余空间」这件事上通常同一类意图。)
对照记:
css
/* 默认值:不抢剩余空间;空间不够时可以缩 */
.item {
flex: 0 1 auto;
}
/* 主动写 flex: 1 后:抢剩余空间;也可以缩 */
.item {
flex: 1 1 0%;
}若你只写:
css
.child {
flex-shrink: 0;
}等价于把第二项改成 0,整体变成 flex: 0 0 auto:不主动吃剩余空间、不允许被 flex 压扁、尺寸跟内容(或你设的宽高)走。
三句常用口诀可以背下来:
css
/* 固定 / 内容撑开:不许压扁 */
.fixed {
flex: 0 0 auto;
}
/* 吃剩余空间;内容多了在本区滚 */
.fill {
flex: 1 1 0;
min-height: 0;
}
/* 就是浏览器默认:可以被别人挤一挤 */
.normal {
flex: 0 1 auto;
}扩展口诀(一条一行):
- 不写
flex:默认0 1 auto——不抢剩余空间,空间不够时可以被压缩。 - 写
flex: 1:常见等价1 1 0%——主动吃剩余空间,也可以缩。 - 写
flex-shrink: 0:不许 flex 在主轴上压扁我(常与其它 longhand 组合成0 0 auto语义)。 - 写
flex: 0 0 auto:按内容(或你设的宽高)来,不抢也不缩。 - 写
flex: 1 1 0+min-height: 0(纵向可滚区):我吃剩余空间,内容多了只在自家滚动。
审核页卡片里:顶部、标题、审核操作、底栏都属于 「必须完整显示」 → 用 .fixed 语义(写 flex: 0 0 auto 最一眼可读;只写 flex-shrink: 0 也行,因为默认 grow 已是 0、basis 仍是 auto,与本页需求一致)。
计算过程(直觉版)
假设父容器总高度 1000px,子元素从上到下排:
先算「由内容撑开」的自然高度
例如:顶部信息 80px、标题区 80px、审核操作区 260px、底部按钮栏 60px——这些都当作「固定占用」,不参与抢剩余高度。扣掉自然高度
剩余 ≈1000 - 80 - 80 - 260 - 60 = 520px(数字仅作示意)。把剩余高度给弹性区
中间摘要区域写了flex: 1 1 0(再配合min-height: 0),就吃这 520px。摘要超出时
摘要区内部滚动,不会把审核区、按钮顶出卡片可视区域。
因此:审核按钮、标签始终可见;摘要尽量多展示,但不挤压操作区。
flex: 1 1 0 在干什么(以及和单写 flex: 1 的关系)
上文已说明:单写 flex: 1 常见即 flex: 1 1 0%,与默认 0 1 auto 的差别主要在 grow 与 basis。中间摘要要占满「扣完固定区之后的剩余高度」,必须显式让 flex-grow 为 1(写 flex: 1 或 flex: 1 1 0 均可落在这一类意图上)。
用长属性写清楚就是:
css
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0;flex-grow: 1:有剩余空间时,由我来吃。flex-shrink: 1:空间不够时,我可以缩(配合内部滚动,而不是去顶兄弟节点)。flex-basis: 0:参与分配剩余空间时,不要先按我的内容高度占位,从 0 开始分「多出来的那一截」。
中间「摘要 / 正文」这类区域适合:
css
.review-card__summary-scroll {
flex: 1 1 0;
}即:我是弹性区,剩多少给多少。
min-height: 0 为什么几乎必写
这是很多人踩坑的点。
Flex 子项默认等价于带有 min-height: auto 的语义:最小高度不能小于内容所需高度。摘要很长时,子项会说「我内容要 1000px,我最少也要 1000px」,结果把整个列撑爆,把下面的操作区挤出可视区域。
所以要写:
css
.review-card__summary-scroll {
min-height: 0;
}含义:允许我比内容更矮;超出部分交给 overflow: auto / scroll-view 在本区域内部滚动。
没有 min-height: 0,单独写 flex: 1 经常「看起来不生效」。更一般的「横轴挤压文字」场景见同目录文:为什么 min-w-0 / min-h-0 能救活被 Flex 卡住的子元素?。
套到你的页面结构(示例)
结构可以接近(小程序/uni-app 常用 view / scroll-view;Web 可把 view 换成 div,scroll-view 换成带固定高度链路的容器 + overflow: auto):
html
<view class="review-card">
<view class="review-card__top">顶部信息</view>
<text class="review-card__title">标题</text>
<scroll-view class="review-card__summary-scroll" scroll-y>
摘要内容
</scroll-view>
<view class="review-mark">审核操作区</view>
<view class="action-bar">确认按钮</view>
</view>对应样式:
css
.review-card {
height: 100%;
display: flex;
flex-direction: column;
}
.review-card__top,
.review-card__title,
.review-mark,
.action-bar {
flex: 0 0 auto;
}
.review-card__summary-scroll {
flex: 1 1 0;
min-height: 0;
}效果归纳:
- 顶部、标题:随内容高度。
- 审核区:随标签、表单项自然撑开,且不被压扁。
- 底栏按钮:固定留在卡片底部可见。
- 剩余高度:全部给摘要。
- 摘要过长:只在摘要区域滚动。
同一套思路也常见于移动端审核台、富文本编辑器、IM 聊天窗口、后台详情抽屉等。
建模时先问自己两句
- 「谁必须完整显示?」 → 这些块用
flex: 0 0 auto(或等价地只写flex-shrink: 0,语义仍是「固定区」)。 - 「谁可以吃剩余空间,内容多了只在自己里面滚?」 → 这一块用
flex: 1 1 0+min-height: 0。
能稳定答这两句,就已经是在选布局算法而不是在随机试属性了。
小结
| 区域 | 典型写法 | 目的 |
|---|---|---|
| 父卡片 | height: 100% + flex + column | 占满外层给定高度,纵向排布 |
| 顶、标题、审核、底栏 | flex: 0 0 auto(或只写 flex-shrink: 0,本页等价) | 对应默认里的 shrink,改为不许压扁 |
| 中间可滚区 | flex: 1 1 0 + min-height: 0 | 吃掉剩余高度,且允许比内容矮、内部滚动 |
记住一句:纵向「上中下」里,只有中间要吃剩余高度并滚动时,中间一定要 min-height: 0(或等价),否则 flex 会优先满足内容最小高度,把下面顶飞。
题外话:这和「会不会背 CSS」不是一回事
前端布局不是「会属性列表就自然会」;更像一套小型物理规则,要把产品意图翻译成正确的布局模型(谁固定、谁伸缩、谁滚动)。AI 能生成片段,但「默认 flex: 0 1 auto 里 shrink: 1 会在空间不够时允许压扁谁」仍要人自己想清楚——这种推理到稳定实现的能力,和会不会失业的简单叙事无关,和单纯「复制一段 CSS」也不是同一回事。
「可以不手写,但不能不会」
以后真正吃香的不一定是「每一行都手写」,更常见的是:
- 看得懂别人(含 AI)写了什么;
- 能判断为什么这么写;
- 知道哪里可能有坑;
- 能把产品意图翻译成结构、状态、数据流和布局模型;
- 关键时候能接管、修正、取舍。
「我可以不写,但不能不会」——说的正是这层:AI 会省掉大量重复劳动,但不会替代判断力。审核、推荐、训练数据、前后端闭环这类事,靠的是系统理解,不是单点抄一段样式。
推荐笔记模板(同类布局一条一记)
以后遇到「固定高 + 底栏常驻 + 中间滚」这类点,可以按下面骨架记笔记。攒几十条后,读 AI 产出会从「好像能跑」变成「知道为什么这么写、哪里不该这么写」,会稳很多。
md
## 问题
父容器固定高度,下面操作区要完整显示,上面内容区吃剩余高度。
## 关键写法
父容器:
```css
display: flex;
flex-direction: column;
```
自然高度区:
```css
flex: 0 0 auto;
```
剩余空间区:
```css
flex: 1 1 0;
min-height: 0;
```
再配合 `overflow: auto` 或小程序 `scroll-view` 等,只在中间区滚动。
## 为什么
Flex 子项默认 `min-height: auto`,内容过长时会按「内容最小高度」参与分配,容易把底部挤出可视区。`min-height: 0` 允许该区比内容更矮,超出部分在**本区内部**滚动。
## 口诀
固定区不缩,内容区吃剩余。能滚的区域:`flex: 1`(或 `flex: 1 1 0`)+ `min-height: 0`。