分类课程智能体AI
文章
订阅
分类课程AI导师
文章
价格
课程进度
15 / 20
上一节CSS 字体系统:从字体回退到可变字体下一节列表、生成内容与计数系统:让浏览器替你维护编号
自在学

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

公网安备湘公网安备43020302000292号 | 湘ICP备2025148919号-1

关于我们隐私政策使用条款

© 2025 - 2026 株洲市自在学教育科技有限公司 版权所有

公网安备湘公网安备43020302000292号湘ICP备2025148919号-1

编程CSS 样式设计CSS 文本排版:从行内方向到纵排书写的完整指南

CSS 文本排版:从行内方向到纵排书写的完整指南

选好字体,只是文字排版的起点。字形怎样组成一行、在哪里换行、遇到图片时如何占位、重点内容如何标记,还要由一组文本属性共同决定。我们在内容页里遇到的许多问题,也藏在这些细节中:正文行高能否随字号变化,链接是否容易辨认,长网址会不会撑破卡片,中文标点又会在哪里断开。

下面先从浏览器使用的排版坐标系讲起,再依次看缩进、对齐、间距、行盒、装饰、阴影、空白、断词和纵排。你可以边读边运行示例,看看每个属性改变的究竟是哪一层排版结果。

学习文本属性时,先问“它控制的是整块内容、行盒,还是某个行内盒”,再问“它是否继承”。这两个问题可以解释绝大多数看似反常的排版结果。


先建立排版坐标系

我们平时会说文字是“横排”或“竖排”,但遇到阿拉伯文、希伯来文或竖排中文,这两个词很难把方向说清楚。CSS 因此用两条轴来描述文字流动:

  • 行内方向:同一行中的文字和行内元素依次前进的方向。简体中文横排通常从左到右,阿拉伯文通常从右到左,竖排中文则从上到下。
  • 块方向:一行结束后,下一行或下一个块放置的方向。普通横排页面通常从上到下,vertical-rl 竖排则从右向左增加新列。

start、end 这样的逻辑值会跟着行内方向变化,left、right 则始终指向固定的物理边。如果页面要容纳不同语言,优先使用逻辑方向,同一套样式就能随书写模式调整位置。

横排与竖排中的行内方向和块方向

css
.article-title {
  text-align: start;
}
 
.article-meta {
  margin-inline-start: auto;
}

这里要分清旋转和竖排。transform: rotate(90deg) 只是把已经排好的横向内容整体转过去;writing-mode 才会重新确定行内轴与块轴,同时改变换行方式、标点朝向和行盒构造。

1
在从右向左书写的横排段落中,text-align: start 通常会对齐到哪里?

缩进、对齐与字符间距

首行缩进和左内边距不是一回事

text-indent 用来移动块盒中的第一行,移动方向沿着行内方向,所以它不一定是向右。这个属性会继承,初始值是 0。中文正文常见的 2em 会随当前字号变化,比固定像素更容易保持两个字宽的缩进。

css
.prose p {
  text-indent: 2em;
}

百分比以包含块的行内尺寸为参照,并不随字号计算;在普通横排中,它通常就是包含块宽度。负值可以做出悬挂缩进,不过第一行可能伸到容器外被裁掉。使用时需要在逻辑起始侧留出内边距。

css
.reference-item {
  padding-inline-start: 3em;
  text-indent: -3em;
}

实验性的 hanging 会缩进第一行之外的各行,each-line 还会让强制换行后的新行获得缩进。它们的浏览器支持需要按项目目标检查。要兼顾稳定性,通常还是使用负缩进配合内边距。text-indent 不直接移动普通行内元素或图片,但行内图片如果出现在第一行,仍会跟着整行一起移动。

2
text-indent: 10% 的百分比通常按当前文字的 font-size 计算。

text-align 与最后一行

text-align 决定块内每一行的行内对齐方式,常见值有 start、end、left、right、center 和 justify。它移动的是块里的文字等行内内容,不会移动块盒本身。固定宽度的卡片要居中,仍应交给外边距、Grid 或 Flex。

css
.quote {
  text-align: center;
}
 
.news-column {
  text-align: justify;
  text-align-last: start;
}

使用两端对齐后,浏览器会拉开可用的词间距,有时也会调整字符间距,让最后一行之外的各行同时贴住两侧。栏宽较窄时,这些被拉大的空隙会连成明显的“空白河流”;中文也可能出现不均匀的字距。因此,正文是否两端对齐要结合栏宽和实际文字判断。

text-align-last 管的是元素最后一行,也包括紧挨着 <br /> 等强制换行之前的那一行。单行段落既是第一行也是最后一行,因此视觉结果会优先采用 text-align-last。如果希望最后一行也两端对齐,兼容性较稳的写法是同时设置 text-align: justify 和 text-align-last: justify。justify-all 表达的是同一意图。另一个较少用到的值 match-parent,会先按父级方向解析父元素的 start 或 end,再把结果用于子元素。

3
关于 text-align 与 text-align-last,哪些说法正确?

词间距、字间距与继承

word-spacing 调整浏览器认定的词间空白,letter-spacing 调整字符之间的距离。两者都接受正负长度,也都会继承。normal 和 0 平时看起来相近,含义却有差别:两端对齐时,浏览器仍可继续调整 normal 的字符间距;显式写成 letter-spacing: 0,则表示不要为对齐再改变这段间距。

css
.eyebrow {
  letter-spacing: 0.12em;
}
 
.english-lead {
  word-spacing: 0.08em;
}

浏览器怎样识别“词”,取决于语言和空白规则。连续中文通常没有空格,所以 word-spacing 对中文正文几乎看不出效果。字距改得过大或过小,还可能破坏连字与字形连接。

继承时还有一个容易漏掉的细节。父元素上的 0.25em 会先按父级字号计算为长度,子元素继承的是这个计算结果。即使子元素字号减半,间距也不会自动减半。想让间距继续保持相同比例,需要在字号变化的子元素上重新声明。

4
要让两端对齐时浏览器不再自行拉伸字符间距,可显式设置 letter-spacing: ____。

你可以在下面同时调整首行缩进、整段对齐、最后一行对齐和字词间距。改变容器宽度后,两端对齐与最后一行的差异会更容易观察。


line-height 与行盒的构造

font-size 决定字形内容区的大致尺寸,line-height 决定连续基线之间的最小距离。用后者减去前者,就得到总行距:

text
总行距 = 计算后的 line-height − 计算后的 font-size
半行距 = 总行距 ÷ 2

浏览器把总行距平分到内容区上下两侧,由此构成行内盒。最终的行盒还要容纳这一行里的其他行内盒、图片和控件:最高的顶部与最低的底部都必须装得下。这样看就容易理解,块元素上的行高只是基线距离的下限;行内放入更高的图片后,实际行盒仍然会被撑大。

内容区、行内盒、基线和行盒的关系

line-height 可以写成 normal、长度、百分比或无单位数值。normal 由字体和浏览器决定,常见结果约为字号的 1.2 倍,但这不是固定公式。正文可先从 1.5 到 1.8 的无单位值试起,再根据字号、字面大小、行长与语言调整。

css
body {
  line-height: 1.65;
}
 
.compact-label {
  line-height: 1.2;
}

无单位值的好处在继承时最明显。假设父级字号为 10px,并设置 line-height: 1em,浏览器会先算出 10px。子元素即使把字号改为 18px,仍可能继承这 10 像素,文字就容易重叠。父级若改写为 line-height: 1.5,子元素继承的是倍率,会按自己的字号算出 27px。

不要用固定高度加 flex 居中来替代正文行高。那只能摆放整个文本盒,不能解决多行文字内部的基线距离、行内图片和上标对行盒的影响。

5
父元素 line-height: 1.5,子元素 font-size: 20px 且未覆盖行高,子元素行高通常是多少?

vertical-align 对齐的是行内盒

vertical-align 主要用于行内元素、::first-letter、::first-line 和表格单元格。普通块盒里的多行正文不会因为它而垂直居中。表格单元格能够识别的值也较少,主要是 baseline、top、middle 和 bottom 等。

  • baseline:元素基线与父级基线对齐;图片没有基线,会用底边对齐基线,因此下方常留出一小段空隙。可按需求改为 middle、bottom,也可以把图片设为块级。
  • sub / super:下移或上移基线,但不会自动缩小字号。真正的上下标往往还要配合较小 font-size。
  • top / bottom:与整条行盒的顶部或底部对齐。
  • text-top / text-bottom:与父元素字体形成的文本区域顶部或底部对齐,忽略同一行里更高的图片。
  • middle:元素中点对齐到父级基线上方约 0.5ex 的位置,并不是容器的几何中心。
  • 长度与百分比:正值上移、负值下移;百分比相对于元素自身的 line-height。移动后的盒子仍参与行盒高度计算,可能把整行撑高。
css
.formula sup {
  font-size: 0.75em;
  vertical-align: 0.45em;
}
 
.icon-with-text {
  vertical-align: -0.125em;
}
6
哪些做法能解释或解决行内图片底部的小空隙?

大小写转换与文本渲染提示

text-transform 只改变显示出来的大小写,不会改写源文本。uppercase、lowercase 和 capitalize 分别显示为全大写、全小写和每个词的首字母大写,none 保持原样。capitalize 只识别词首,不理解标题的编辑规则,也不会自动判断冠词是否该小写。词边界与大小写映射都依赖语言,所以页面还要正确设置 lang。

full-width 会把可用字符映射为全角形式,便于和方形表意文字网格协调;full-size-kana 会把小假名转换为完整尺寸,多用于注音场景。使用这些值之前,要检查目标浏览器的实际支持情况。

css
.product-code {
  text-transform: uppercase;
}
 
:lang(en) .section-kicker {
  text-transform: capitalize;
}

全大写会改变单词轮廓,段落一长就更难扫读。部分辅助技术还可能把全大写内容当成缩写逐字朗读,所以这种样式更适合短标签,不宜用于关键长文。

text-rendering 最初用于表达矢量文本的渲染倾向,常见值有 auto、optimizeSpeed、optimizeLegibility 和 geometricPrecision。它不能保证各平台得到相同结果,连字、字偶距和提示信息仍由操作系统、字体与浏览器共同决定。现代设备上,手动设置这个属性通常变化不大,是否使用要以真实设备测试为准。

7
text-transform: capitalize 会按照语言的标题语法,自动判断哪些冠词不该大写。

把文本装饰拆开来看

文本装饰由几项可以独立控制的属性组成:

css
a:any-link {
  text-decoration-line: underline;
  text-decoration-color: currentColor;
  text-decoration-style: solid;
  text-decoration-thickness: from-font;
  text-underline-offset: 0.14em;
  text-decoration-skip-ink: auto;
}

text-decoration-line 可以组合 underline、overline 和 line-through,已经废弃的 blink 不应再用。text-decoration-color 默认取 currentColor。text-decoration-thickness 可以写固定长度、字号百分比、auto 或 from-font,其中相对单位会随着字号缩放。text-decoration-style 支持 solid、double、dotted、dashed 和 wavy。

简写 text-decoration 可以同时写线型、样式、颜色和粗细:

css
.spell-error {
  text-decoration: wavy #d33 underline 0.1em;
}

使用简写时,没写出的子属性会被重置,新的简写也会替换旧装饰,不会自动叠加。同一元素上的多条装饰线共用颜色和样式。下划线与上划线如果要用不同颜色,需要用嵌套元素分别绘制。

text-underline-offset 只调整下划线的位置。正值会让它沿块方向远离文字,负值则让它靠近。text-decoration-skip-ink: auto 允许浏览器在下划线经过字形下降部时暂时断开,none 强制连续绘制,all 要求跳过字形,但需要留意兼容性。

装饰属性本身不会继承,父元素画出的线却会贯穿后代。对子元素设置 text-decoration: none,擦不掉父元素的线,因为这条线原本就不是子元素绘制的。用背景色画一条同位置、同粗细的装饰线可以遮住它,但这种处理很容易随背景或字号变化而失效。更稳妥的做法是调整 DOM,让装饰落在真正需要它的元素上。

正文链接去掉下划线后若只靠颜色区分,会让色觉障碍用户和快速扫读者难以发现链接。交互状态至少要保留形状、粗细或其他非颜色线索。

8
要优先采用字体文件建议的装饰线粗细,可设置 text-decoration-thickness: ____。

阴影、着重号与绘制顺序

一层 text-shadow 包括横向偏移、纵向偏移、可选的模糊半径和颜色,多层阴影之间用逗号分隔。省略颜色时通常会取 currentColor。阴影只影响绘制,不参与盒模型,也不会增加行高,所以它可以超出元素的视觉边界。

css
.hero-title {
  color: white;
  text-shadow:
    0 1px 2px rgb(0 0 0 / 55%),
    0 0 10px rgb(0 0 0 / 30%);
}

浅色文字放在明暗变化较大的背景上时,一层范围较小的阴影可以补足边缘对比。阴影层数太多、模糊半径过大,或让阴影持续动画,都会增加绘制成本,尤其要在移动设备上测试。

中文排版还可以用 text-emphasis 在可标记字符旁放置着重号。text-emphasis-style 支持实心或空心的点、圆、双圆、三角与芝麻点,也可以传入单字符字符串;多字符字符串可能只采用首字符。text-emphasis-color 默认跟随文字颜色。text-emphasis-position 在横排中控制 over / under,在竖排中控制 left / right。样式和颜色可以写进 text-emphasis 简写,位置没有放进简写,这样修改样式时不会顺手重置排版位置。

css
.key-sentence {
  text-emphasis: filled sesame #c83b31;
  text-emphasis-position: under right;
}

着重号会继承。为了避开相邻行,它还可能增加行高;空格、标点和控制符通常不会带着重号。浏览器大体按阴影、下划线、上划线、文字、着重号、删除线的顺序从后向前绘制。几个效果重叠时,可以用这个顺序判断谁会盖住谁。

文字阴影、装饰线、正文与着重号的绘制层次

9
下列哪种效果可能直接增加所在行的行盒高度?

空白处理与制表符

HTML 默认会折叠连续空格,也会忽略源码中的换行。white-space 把三个决定组合在一个属性中:空白是否折叠,源码换行是否保留,内容是否允许自动换行。

  • normal:折叠空白,忽略源码换行,允许自动换行。
  • nowrap:折叠空白,但禁止普通软换行;<br /> 仍可强制换行。
  • pre:保留空白与源码换行,禁止自动换行。
  • pre-wrap:保留空白与源码换行,同时允许自动换行;行尾空格可以悬挂在行尾。
  • pre-line:折叠连续空白,保留源码换行,并允许自动换行。
  • break-spaces:像 pre-wrap 一样保留空白,但每个保留的空格后都可断行,行尾空格也占据尺寸并影响 min-content / max-content。
css
.terminal-output {
  white-space: pre-wrap;
}
 
.user-note {
  white-space: break-spaces;
}

只有制表符没有被空白规则折叠时,tab-size 才会显示出效果。整数表示一个 Tab 相当于多少个空格,初始值通常为 8;长度值则直接指定制表宽度。这个属性会继承,负值无效。

css
pre {
  tab-size: 4;
}
10
哪些 white-space 值会保留源码中的连续空格?

换行、断词与连字符

一行文字在哪里断开,是几组规则依次判断的结果。white-space 先决定能否软换行;语言、word-break 和 line-break 再提供正常断点;如果一长串内容仍然放不下,overflow-wrap 才会介入。hyphens 负责决定断词处是否显示连字符。

文本空白、换行、长词断开和连字符的决策路径

hyphens 的初始策略是 manual,只响应软连字符 U+00AD 等人工提示;none 会连人工提示也忽略;auto 则允许浏览器依据语言词典断词。自动断词依赖 lang、词典和浏览器,各平台的断点不一定相同。<wbr /> 只提供断行机会,不会在行尾显示连字符;软连字符只有真正发生断行时才会出现。

html
<p lang="en" class="essay">Antidisestablishmentarianism...</p>
css
.essay {
  hyphens: auto;
}
 
code,
kbd,
textarea,
input {
  hyphens: none;
}

如果在 body 上开启自动断词,记得为代码、变量、命令和输入框关闭它。hyphens 会继承,而命令中多出的一个连字符,就可能让复制后的内容无法运行。

word-break 用来确定普通软断点:

  • normal 按语言规则断行。
  • break-all 几乎可在任意字符间断开,哪怕把英文单词切碎,并且不会自动显示连字符。
  • keep-all 抑制中日韩文本在字符间断开,连续文本可能溢出。
  • 旧值 break-word 已废弃,其历史效果近似 word-break: normal; overflow-wrap: anywhere。

line-break 进一步控制中日韩字符与标点附近的断行。loose 规则较宽松,适合窄栏;normal 使用常见规则;strict 更严格;anywhere 会在每个排版单元附近提供断行机会,并且不做连字。标点的具体表现仍可能随浏览器和语言而变化。

overflow-wrap 会等到不可断字符串确实要溢出时再介入:

  • normal 不额外制造断点。
  • break-word 必要时在词内断开,但这些临时断点不参与 min-content 内在宽度计算。
  • anywhere 同样可在词内断开,并把这些断点纳入 min-content 计算,因此网格或弹性布局里的卡片更容易真正缩小。

word-wrap 是 overflow-wrap 的历史别名。相比 word-break: break-all,overflow-wrap 会先使用正常的空白断点,只有内容确实放不下时才切开长词,因此更适合需要保留自然阅读节奏的正文。

用户生成内容卡片的稳健起点通常是 overflow-wrap: anywhere;自然语言长文则应先设置正确的 lang,再评估 hyphens: auto。代码内容应优先水平滚动,避免把标识符切得无法复制。

11
当 width: min-content 时,overflow-wrap: anywhere 与 break-word 可能得到不同宽度。

纵排、字符朝向与双向文本

writing-mode 会改变块流方向:

  • horizontal-tb:行内方向水平,新行从上到下排列。
  • vertical-rl:文字沿竖直行内轴前进,新列从右向左排列,传统中文竖排常用。
  • vertical-lr:文字竖直前进,新列从左向右排列。
  • sideways-rl / sideways-lr:把横排文字整体侧转并按相应块方向推进,需检查目标浏览器支持。
css
.vertical-caption {
  writing-mode: vertical-rl;
  text-orientation: mixed;
}

切换到竖排后,vertical-align 仍然相对行盒移动行内元素,只是视觉上可能表现为水平位移,不能再按属性名直觉理解。竖排行内字符的朝向由 text-orientation 决定:mixed 通常让表意文字直立、拉丁文字侧转;upright 让所有字符直立;sideways 让所有字符侧转。

text-combine-upright 可以把竖排中的短数字压进一个全角字符的空间。处理日期时,可以把两到三位数字单独放进 <span>,再设置 all,这样比作用于整段更可控。对整段使用会把过多字符挤在一起。digits 2、digits 3、digits 4 表达得更精确,不过上线前仍要在目标环境中验证。

html
<p class="date-vertical">七月<span class="combine">13</span>日</p>
css
.date-vertical {
  writing-mode: vertical-rl;
}
 
.combine {
  text-combine-upright: all;
}

从右到左或混合方向的内容,优先在 HTML 中写清语义:使用 dir="rtl"、dir="ltr"、dir="auto",需要强制覆盖时再用 <bdo>。CSS 的 direction 会影响文本方向、表格列顺序、横向溢出方向以及两端对齐的最后一行。行内元素还要结合 unicode-bidi: embed 或 bidi-override,才会以特定方式改变双向算法。unicode-bidi: normal 不会新增嵌入层,embed 会新建方向嵌入,bidi-override 则忽略隐式双向重排,使用风险最高。

文本方向属于内容语义。若样式表加载失败,HTML 的 dir 仍能保护阅读顺序;只用 CSS direction 和 unicode-bidi,可能让样式失效时的文本含义发生变化。

12
传统中文竖排通常使用 writing-mode: ____,使新列从右向左增加。

上线前容易漏掉的问题

属性值通常查一下就能确认,真正容易出错的是把属性用在了错误的盒子或场景里。上线前可以按下面这份清单逐项检查:

  1. 用 text-align: center 居中卡片。 它只居中卡片里的行内内容;卡片本身要用 Grid、Flex 或逻辑外边距。
  2. 把 vertical-align: middle 当块级垂直居中。 它对普通块布局不起这个作用,且行内 middle 也不是几何中心。
  3. 全局固定像素行高。 子元素字号变化后仍继承固定计算值,容易重叠;正文容器优先用无单位倍率。
  4. 给所有文字 word-break: break-all。 它会在已有更好断点时仍切碎英文;用户内容优先考虑 overflow-wrap: anywhere。
  5. 用 white-space: nowrap 做省略号却漏掉约束。 单行省略通常还要 overflow: hidden、text-overflow: ellipsis 和可收缩的宽度;在 Flex 子项里常需 min-width: 0。
  6. 去掉链接下划线,只剩颜色。 这会削弱可发现性与无障碍。
  7. 在父元素取消装饰,期待子元素能擦掉父级下划线。 父级绘制的线会贯穿后代,子级的 none 无法撤销。
  8. 依赖自动断词却不设置 lang。 浏览器没有语言线索,就无法选择正确词典与标点规则。
  9. 把视觉竖排做成旋转。 旋转不改变断行、标点和字符朝向,真正排版要使用 writing-mode。
  10. 只在一个浏览器检查复杂文本。 字体度量、连字、自动断词、装饰跳墨和 CJK 断行都有实现差异。
13
为一行超长标题实现可靠省略号,通常需要哪些条件?

按排版机制选择属性

实际写样式时,可以沿着一条清楚的路径排查。先确认语言和书写方向,再处理块内对齐与缩进;随后用无单位行高建立稳定的行盒距离。需要强调内容时,先保证链接和重点容易辨认。面对用户输入,再分别考虑空白、正常断点、应急断点与连字符。样式写完后,还要放进真实字体、窄屏和多语言内容中检查。

css
.readable-prose {
  line-height: 1.65;
  text-align: start;
  overflow-wrap: anywhere;
}
 
.readable-prose:lang(en) {
  hyphens: auto;
}
 
.readable-prose a:any-link {
  text-decoration: underline currentColor from-font;
  text-underline-offset: 0.14em;
  text-decoration-skip-ink: auto;
}

不存在适用于所有场景的文本属性组合。我们需要回到内容本身做判断:行高要配合字号与行长,断行规则要保护容器,逻辑方向要跟随语言,链接装饰则要提供颜色之外的识别线索。HTML 中的语言与方向语义也应写完整,这样即使样式加载失败,内容顺序仍然正确。

14
设计一个多语言正文组件时,最稳妥的第一步是什么?
  • 先建立排版坐标系
  • 缩进、对齐与字符间距
    • 首行缩进和左内边距不是一回事
    • `text-align` 与最后一行
    • 词间距、字间距与继承
  • `line-height` 与行盒的构造
  • `vertical-align` 对齐的是行内盒
  • 大小写转换与文本渲染提示
  • 把文本装饰拆开来看
  • 阴影、着重号与绘制顺序
  • 空白处理与制表符
  • 换行、断词与连字符
  • 纵排、字符朝向与双向文本
  • 上线前容易漏掉的问题
  • 按排版机制选择属性

目录

  • 先建立排版坐标系
  • 缩进、对齐与字符间距
    • 首行缩进和左内边距不是一回事
    • `text-align` 与最后一行
    • 词间距、字间距与继承
  • `line-height` 与行盒的构造
  • `vertical-align` 对齐的是行内盒
  • 大小写转换与文本渲染提示
  • 把文本装饰拆开来看
  • 阴影、着重号与绘制顺序
  • 空白处理与制表符
  • 换行、断词与连字符
  • 纵排、字符朝向与双向文本
  • 上线前容易漏掉的问题
  • 按排版机制选择属性