← 首页 / 🧲 Flexbox · 简写语义与对齐边界 待审核 14 页

🧲 Flexbox · 简写语义与对齐边界

Flexbox 入门容易,但两条主线都藏着易被忽略的细节。其一是 flex 简写本身的语义:flex: 1 / auto / none 各展开成什么、flex-basiswidth 谁优先、为什么 flex: 1 的 basis 是 0% 而不是 0。其二是「对齐」在边界情形下的行为:justify-content: center 一旦溢出为何把左半边永久截断safe / unsafe 如何化解、多行 flex-wrap: balance 又如何把各行调匀。每页都用真实 getComputedStyle / getBoundingClientRect 实测呈现浏览器的实际行为。

系列脉络: 先打地基——flex 简写如何展开成三个全写值、flex-basis0 vs 0% 与 vs width → 看分配过程:grow / shrink 如何按比例瓜分剩余 / 收缩亏空、由此引出 等宽列假象(flex: 1 等分的是内容盒、非列宽)、以及让 grow 正比于 basis 实现的 两端对齐图片墙 → 退一步看 width / min / max 的取值空间(length / % / 内容驱动关键字、百分比相对谁)→ 再看谁能成为 flex item:display: contents 穿透中间包裹层 → 再看排布:对齐属性全景(谁管主轴、谁管交叉轴)、gap 与缝里的分隔线order / reverse 只改视觉序的 a11y 陷阱 → 再到边界:「居中即截断」(scrollLeft 到不了左侧)、滚动容器丢尾部 padding → 用 safe center 一行化解 → 行间维度的 flex-wrap: balance 让多行换行更均衡。 shorthand · 语法

flex 简写:一个值如何展开成三个

flex: 1 / auto / none / 100px 都是简写,恒定展开为 flex-grow flex-shrink flex-basis 三个全写。本页理清各关键字的展开规则、三者的默认值(0 / 1 / auto),以及单值是 <number> 还是 <width> 如何分派;切换写法用 getComputedStyle 实测浏览器真正展开的值(含 flex: 1 的 basis 实为 0%)。

flex-basis · 主轴起点

flex-basis:vs width、百分比与 content 回退

flex-basis 定 item 主轴初始尺寸,有三处易错点:与 width 同时存在时 flex-basis 胜出;百分比在容器尺寸不确定时悄悄退回 content;由此 flex: 1(= 1 1 0%)里的 0% 不等于 0。用列向无定高容器 getBoundingClientRect 实测 0 vs 0% 的高度差,再切 basis/width 看实测宽度跟谁走。

grow / shrink · 空间分配

flex-grow / flex-shrink:剩余与亏空空间如何分配

把各 item 的 flex-basis 求和与容器主轴一比:有剩余flex-grow 按比例瓜分、不足flex-shrink 按 shrink×basis 加权收缩,两者互斥。拖容器宽越过临界点,用预测算式getBoundingClientRect 实测逐项对照;并讲清 min-width: auto 下限与 wrap 为何会让 shrink「失灵」。

equal columns · 等宽假象

等宽列假象:flex: 1 给的是「内容盒等宽」,不是「列等宽」

给每列 flex: 1 做等宽栅格,一旦某列有不同的 padding / border明显偏宽。根因接着分配机制:flex-grow 把剩余空间等量加到各列 content box,padding / border 不计入分配、是事后叠加的——于是「内容盒都等宽,边框盒差 2×padding」。用 getBoundingClientRect 实测对照 flex: 1 与 grid 1fr:两种排布下「边框盒 / 内容盒谁等宽」正好对调,并给出 flexbox 内的嵌套补救与 grid 首选解。

justified gallery · 实战

两端对齐图片墙:让 flex-grow 正比于 flex-basis

相册 / 画廊版式要的「同一行等高、各图宽随宽高比、整行两端撑满」,落到 grow / basis 上只需一招:令每个 item 的 flex-basisflex-grow 同时等于该图缩到统一高度时的宽度 wᵢ = H×宽/高。代入分配公式得 finalᵢ = wᵢ×(1 + free/Σw)——同行系数 k 相同,整行等价于等比缩放、行内自动等高。拖容器宽 / 基准行高,用 getBoundingClientRect 逐行实测等高;并讲清末行为何要 ::after { flex-grow: 99999 } 收尾,以及与 JS 二分方案的取舍。

sizing · 尺寸取值

尺寸取值:width / height / min / max 的各种值

退一步看尺寸属性本身能接哪些取值:固定 <length>、相对 <percentage>、内容驱动的 min-content / max-content / fit-content()、较新的 stretch。实测 width 取值之旅 + min/max 夹取;讲清百分比相对包含块哪个维度(含 padding-top:% 按宽度算、height:% 在父高不确定时退化为 auto 的陷阱)、flex item 的 min-width: auto 自动最小尺寸、逻辑尺寸 inline-size / block-sizewriting-mode 翻转,以及与 layout 相关的长度单位(rem/ch/vw/svh/dvh/容器单位 cqw)。

display: contents · 穿透

display: contents:让包裹层「消失」,子元素直接成为 flex item

flex / grid 只认直接子元素。当容器与真正的内容之间隔着一层语义包裹(组件根、列表项 wrapper)时,这层会成为唯一的 flex item、把内容「闷」在里面。display: contents 让这层自身不生成盒子、子元素上移直接成为 item。实测切 blockcontents 看子元素如何进入 flex 行并让 flex: 1 生效;并讲清代价(自身 border/padding 失效)、a11y 历史问题、对 replaced/table 无效、vs display: none

alignment · 对齐属性

Flex 下的对齐属性:主轴、交叉轴与作用范围

justify-*主轴align-*交叉轴:justify-content 分配主轴剩余空间,align-items / align-self / align-content 管交叉轴(其中 align-content 仅多行有效)。切 flex-direction / flex-wrap 与各对齐值实时看布局并回显 computed 值;并理清 justify-items / justify-self 为何在 flex 不生效、单项主轴对齐改用 margin: auto

gap · 间距与装饰

gap:项之间的固定间距,以及在缝里画线

gap 只落在项之间、不碰首尾、不参与剩余空间分配(先扣间距再让 grow/shrink 分配 free)——实测它与 margin 拼间距的差别。再看 CSS Gaps Module Level 1 的 gap decorations:把 multicol 的 column-rule 扩展到 flex/grid、补上 row-rule,直接在缝里画分隔线(纯视觉、不占布局),含 rule-break / repeat() 图案与特性探测降级。

order / reverse · 视觉顺序

order 与 reverse:只改视觉顺序,不改源码顺序

order(默认 0,升序排、同值按 DOM 序)与 flex-direction: *-reverse 都重排视觉位置,却不动 DOM / Tab 焦点 / 朗读顺序。调 order 看视觉重排、走一遍 Tab 看焦点始终按 DOM,直观呈现这条「视觉序 ≠ 源码序」裂缝——它正是 WCAG 2.4.3 Focus Order / 1.3.2 Meaningful Sequence 点名的 a11y 陷阱。

overflow · 典型问题

居中即截断:justify-content: center 与 overflow 的冲突

想让横向条「数量少时居中、数量多时可滚」,于是写 justify-content: center + overflow: auto——结果左半边滚不到、被永久截断。根因在 scrollLeft 只能取 [0, scrollWidth-clientWidth]、到不了负值,而居中把首项推到了内容原点左侧。拖动到溢出、点「滚到最左」实测被切的 item,再看两种常见解法:子项 margin: auto、嵌套 + max-width: 100%

overflow · padding 丢失

滚动容器的尾部 padding 去哪了

给会横向滚动的 flex 容器加 padding: 0 20px,滚到最右时尾部那 20px 常常不见了——根因是历史上浏览器算 scrollWidthpadding-inline-end 排除在外,它落在滚不到的区域。滚到两端用 getBoundingClientRect 实测当前浏览器首尾留白,再给三种绕法(末尾 ::after 占位 / 末项 margin / border 代替)。与居中即截断同族、不同因。

safe / unsafe · 规范解

safe / unsafe:给对齐加一句「别切到内容」

Box Alignment 的 <overflow-position> 修饰位:safe center =「优先居中,但若会溢出丢失就退回 start」,正是「居中即截断」的简洁解;unsafe center =「即使切到内容也保持居中」。同一条轨道切换三种关键字实测可达性,回显 getComputedStyle 生效值;再讲它在 align-items/align-content/justify-self 的适用范围,以及 space-between fallback 不退回 start 的反直觉行为(csswg #11937)。

flex-wrap: balance · 提案

flex-wrap: balance:让换行分布更均衡

flex-wrap: wrap 的换行是贪心的——尽量填满当前行,末行常常只剩一两项。提案中的 flex-wrap: balance(csswg #3070)类比 text-wrap: balance,用 Knuth–Plass 式断行让各行长度尽量接近。左右双列对照贪心 vs 均衡(各行标注实测宽度),观察末行如何被调匀;并理清它与 justify-content 分属行间 vs 行内两件事。原生尚未实现,本页用 JS 模拟并加以说明。

这三处的共同主线。它们都落在同一条主线上:当对齐 / 换行遇到「放不下」,浏览器的默认决定未必符合预期。居中默认牺牲可访问性(截断)、贪心换行默认分布不均——而 CSS 一直在补充能显式表达意图的关键字(safe 表达「不要切到内容」、balance 表达「各行调匀」)。理解这一层后,这类现象就都有了清晰的解释。

🔗 相关链接

📐 规范 / 文档

🎨 实例与文章(本系列灵感来源)