先记住这个答案
块级排版有两根轴。块轴是块一个接一个堆叠的方向,行内轴是一行文字排列的方向。横排时块轴从上到下,行内轴从左到右。外边距合并、宽度填满这些规则,本来就是按轴定义的:外边距沿块轴合并,auto 尺寸沿行内轴填满。所以 writing-mode: vertical-rl 把块轴转成从右到左以后,合并的就变成左右外边距,填满父容器的变成高度。direction: rtl 只把行内轴从左往右翻成从右往左,块轴没动,上下外边距照样合并。
- 块轴是块堆叠的方向,行内轴是文字排列的方向
- 书写模式与文本方向分工不同
- 外边距合并永远沿块轴发生
width是物理属性,竖排后它变成块方向的尺寸
竖排后合并的是左右外边距
把一个容器设成竖排,里面放两个块,给它们设左右外边距。
vertical-rl 下的两个块HTML<style>
.box { writing-mode: vertical-rl; height: 200px; }
.a { margin-left: 20px; } /* 竖排时块从右往左排,.a 在右边 */
.b { margin-right: 30px; } /* .b 在 .a 左边 */
/* 两块之间的距离是 30px,不是 50px:左右外边距合并了 */
</style>
<div class="box">
<p class="a">第一段竖排文字</p>
<p class="b">第二段竖排文字</p>
</div>vertical-rl 里块从右往左堆叠,.a 的左外边距和 .b 的右外边距挨在一起,就像横排时上一个块的 margin-bottom 碰到下一个块的 margin-top,于是合并成两者中较大的 30px。反过来,这时 .a 和 .b 的上下外边距各算各的,不再合并。
p 默认带的是 margin-block 方向的外边距,也就是逻辑属性。竖排时它自动落到左右两侧,所以默认样式下段落之间的间距看起来和横排一样。
direction 为什么不影响合并
direction 的作用范围很小:它决定行内轴从哪一头开始,也就是文字、行内元素从左往右还是从右往左排,另外影响表格列的顺序和 text-align: start 落在哪边。块轴依然是从上到下。
所以阿拉伯语、希伯来语页面设了 direction: rtl,段落之间的上下外边距照样合并。变化的是行内方向的逻辑属性:margin-inline-start 在 ltr 下是左外边距,在 rtl 下就变成右外边距。
- 物理属性和逻辑属性
margin-left、width这类是物理属性,永远指屏幕上的某个方向,不跟着书写方向转。margin-inline-start、inline-size、block-size是逻辑属性,按当前的轴来解释。做多语言、要支持竖排的组件,用逻辑属性写一遍,切换writing-mode和direction时样式会自动跟着转。
百分比和 auto 宽度怎么变
width 是物理属性,它的百分比永远相对包含块的宽度,这一点不会因为竖排而改变。变的是它在布局里的角色。
横排时 width 是行内方向的尺寸,块级元素的 width: auto 会填满父容器。竖排时 width 变成块方向的尺寸,auto 不再填满,而是由内容撑开,填满父容器的变成了 height。所以横排里那些关于 height: 100% 不生效的问题,竖排时会换到 width 上出现:包含块的宽度如果也是由内容决定的,百分比宽度可能算不出来。
外边距和内边距的百分比,CSS2 规定一律相对包含块的宽度。书写模式规范把它改成相对包含块的行内尺寸,竖排时就是相对高度。这一条各浏览器的实现要以实测为准,项目里尽量不要依赖竖排下的百分比外边距。
容易答错的地方
- 以为
direction: rtl也会转块轴 direction只翻转行内方向。想让块从左往右或从右往左横着排,要用writing-mode。- 以为外边距合并永远只发生在上下
- 合并是沿块轴发生的。横排时块轴竖直,所以是上下;竖排时块轴水平,就是左右。
- 以为竖排后
width: auto还会填满 - 竖排时填满父容器的是行内方向,也就是高度。
width这时由内容决定,要固定宽度得显式写值或用block-size。
面试官还会怎么问?
做 RTL 适配时,样式要怎么改?
先把 margin-left、padding-right、left 这类物理属性换成 margin-inline-start、padding-inline-end、inset-inline-start。然后在根元素上切换 dir="rtl",布局会自动镜像,不用再维护一份 RTL 样式表。
父元素横排、子元素竖排时,外边距还合并吗?
父子块轴互相垂直,这种情况叫正交流。规范规定,书写模式和父元素不同的块会自动建立新的块格式化上下文,也就是 BFC,所以它和父元素之间的外边距不会合并。它和同样横着排的兄弟之间,上下外边距还是照常合并。
Flex 布局和 writing-mode 有什么关系?
flex-direction: row 的「行」指的是行内轴,不是屏幕上的水平方向。竖排容器里写 row,子项会从上往下排。Grid 的行列也是按逻辑方向定义的。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。