若要为由 <br> 标签分隔且会自动换行的条目设置悬挂缩进,请对其块容器应用 text-indent: 2em hanging each-line。请确认浏览器同时支持这两个关键字;如果需要兼容较旧的浏览器,请为每个条目设置独立的块,并使用内边距和负缩进。
本指南适用于在 Mac 上编辑小型 HTML 页面,并希望每个条目都从左边缘齐头开始,而续行则向内缩进的用户。示例使用合成文本,只需纯文本或代码编辑器以及浏览器即可操作。
为什么普通悬挂缩进在换行标签后会失效
这里需要考虑两种不同的换行。<br> 会创建显式换行。软换行则发生在浏览器将文本适配到可用宽度时。你想要的版式将第一种视为另一个条目的开始,将第二种视为续行。WHATWG 将 <br> 定义为换行;它不会创建新的段落或条目容器。HTML Standard: The br element。
普通的负 text-indent 会影响块的第一个排版行。因此,添加内边距可以为该块创建悬挂缩进,但不会在每个 <br> 后重新设置凸排。后续条目仍属于同一个块。MDN: text-indent。
CSS 规范定义了此处所需的组合:each-line 包含首行以及紧跟强制换行后的各行;hanging 会反转这一选择。两者结合后,软换行的续行会缩进,而每个显式起始行都保持齐头。W3C: First Line Indentation。
目标效果如下。下面的行尾仅作示意;浏览器会根据可用宽度和字体度量选择实际换行位置。
条目 A 从外侧边缘开始,并在文字延续时
当可用宽度不足时向内缩进。
条目 B 也从外侧边缘开始,并且有
自己缩进的续行。
条目 C 很短。
修改页面前选择一种方法
根据现有标记和浏览器要求选择方法。此决策表基于有文档记载的缩进行为和浏览器支持数据,是实现方面的参考,而不是一项独立的浏览器功能。
| 你的情况 | 建议方法 |
|---|---|
| 显式换行属于同一段文本,且目标浏览器支持足够 | 保留换行并使用 hanging each-line |
| 条目是独立的段落或列表项 | 为每个条目使用适当的结构标记 |
| 所有较旧浏览器都必须显示悬挂缩进 | 每个条目使用一个块,并结合内边距和负缩进 |
| 你只能编辑 CSS,且较旧浏览器不支持这些关键字 | 保持易读且齐头的布局,或请求修改标记 |
不要根据对基本 text-indent 的支持情况推断浏览器支持这些关键字。MDN 当前的兼容性数据列出:Safari 15、Firefox 121 和 Chrome 146 起支持 hanging 和 each-line。较旧版本可能支持普通缩进,却不接受这种组合。MDN 浏览器兼容性数据:text-indent。
还要检查这些换行是否表达了正确的结构。WHATWG 规定 <br> 用于内容本身需要的换行,并指出它不得用于分隔段落中的主题组。独立段落应使用段落元素;列表则应使用列表标记。HTML Standard: The br element,HTML Standard: Grouping content。
使用原生 CSS 方案保留换行标签
下面的完整 HTML 文档演示了保留换行的做法。在 Mac 上,将其粘贴到纯文本或代码编辑器中,保存为 hanging-indent.html,然后在浏览器中打开该文件。请使用 HTML 文件,而不是富文本文件。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Break-separated hanging indent</title>
<style>
body {
margin: 2rem;
font-family: system-ui, sans-serif;
line-height: 1.6;
}
.entries {
max-inline-size: 30rem;
margin: 0;
white-space: normal;
text-align: start;
text-indent: 0;
}
@supports (text-indent: 2em hanging each-line) {
.entries {
text-indent: 2em hanging each-line;
}
}
</style>
</head>
<body>
<h1>Sample entries</h1>
<p class="entries">Entry A: A long sample line describing a pale rectangular panel, a narrow heading area, and a small square beside the heading.<br>
Entry B: Another long sample line describing a circular tile, an open space beneath it, and a row of neutral labels.<br>
Entry C: A short sample line.</p>
</body>
</html>
@supports 条件会在应用声明前检查其是否完整受支持。不支持此组合的浏览器会保留明确设置的齐头布局。特性查询会根据浏览器支持情况有条件地应用 CSS。MDN: @supports。
将缩进应用到 .entries,即包含文本的块。相关属性适用于块容器;把声明放在 <br> 上无法为后续条目提供容器。MDN: text-indent。
2em 是一个可调整的设计选项。检查窄屏布局后,可以尝试较小或较大的值。它会创建一致的续行偏移量;它不会测量“条目 A:”的宽度,也不会自动将文本对齐到可变长度标签后的第一个词下方。
如果所有浏览器都需要显示缩进,请使用独立块
如果每个条目都是独立段落,请修改标记,让每个条目都有自己的首行。下面这个完整替代方案移除了分隔换行,并分别对每个段落应用传统的悬挂缩进计算。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Paragraph hanging indents</title>
<style>
body {
margin: 2rem;
font-family: system-ui, sans-serif;
line-height: 1.6;
}
.entries {
max-inline-size: 30rem;
text-indent: 0;
}
.entry {
margin: 0;
padding-inline-start: 2em;
text-indent: -2em;
white-space: normal;
text-align: start;
}
</style>
</head>
<body>
<h1>Sample entries</h1>
<div class="entries">
<p class="entry">Entry A: A long sample paragraph describing a pale rectangular panel, a narrow heading area, and a small square beside the heading.</p>
<p class="entry">Entry B: Another long sample paragraph describing a circular tile, an open space beneath it, and a row of neutral labels.</p>
<p class="entry">Entry C: A short sample paragraph.</p>
</div>
</body>
</html>
内边距会将段落内容的起始位置向内移动 2em。负缩进则将首行向回移动相同的距离。换行后的各行会保留内边距所设定的起始位置。两个值必须相等:只更改其中一个会改变首行的起始位置。MDN 说明了负缩进的用法,而 padding-inline-start 会在逻辑起始边缘设置内边距。MDN: text-indent,MDN: padding-inline-start。
以 16 像素字体大小为例,2em 的偏移量相当于 32 像素:续行向内缩进 32 像素,首行调整量则为 −32 像素。这是示例中的计算结果,并非实际渲染测量值。
采用此结构时,请移除旧的分隔符。将每个完整条目及其现有的行内格式保留在新的段落中。如果内容是列表,请改用列表项;段落示例仅适用于独立段落。HTML Standard: Grouping content。
完成前检查换行和文本流
将页面调整到足以让两个较长条目都发生换行的宽度进行测试。然后扩大窗口,直到它们都能显示为单行。使用以下检查来区分缩进问题与换行或结构问题。
- 检查每个条目的起始位置。 在保留换行的示例中,每个
<br>后的文本都应回到外侧边缘。如果没有,请检查浏览器是否支持完整的关键字组合。 - 检查每个续行。 较长条目应向内缩进。较短条目应保持齐头,不带额外前导空格。
- 检查预期使用的最窄栏宽。 如果缩进占用了太多可用行宽,请减小缩进。
- 检查实际内容。 将内容移入独立块时,保留每个条目的文字、行内强调格式以及预期的显式换行。
如果没有任何内容换行,请检查 white-space。示例使用 normal,它会折叠源代码中的空白并允许换行。pre 会保留空白,但阻止普通软换行;pre-wrap 会保留空白,同时允许换行。如果没有形成续行,缩进就不会显现。MDN: white-space。
避免通过添加空格来模拟缩进。在 white-space: normal 下,连续空格会折叠。也要避免在当前窗口的换行位置插入额外的换行标签:这会将浏览器选择的续行变成显式起始行,从而改变 each-line 对它们的处理方式。MDN: white-space,W3C: First Line Indentation。
让浏览器负责断行和字形塑形。不要将条目拆成字符包装元素来计算可见行数。W3C 记录了行内边界中断塑形的情况,其中包括字符之间存在非零行内内边距、边框或外边距。对于此任务,在预期边界处整体换行条目可以避免在词语内部引入这些额外边界。W3C: Shaping Across Element Boundaries。
最后,选择与内容相符的结构:对于有意义的显式换行,保留换行并使用 hanging each-line;对于独立条目,则为每个条目设置自己的块。确认每个条目都齐头开始,并且只有自然换行的续行会在预期使用的宽度下向内缩进。
来源
- CSS Text Module Level 3 — W3C。
- HTML Standard, Edition for Web Developers: Text-level semantics — WHATWG。
- HTML Standard, Edition for Web Developers: Grouping content — WHATWG。
- text-indent CSS property — MDN Web Docs。
- Browser compatibility data: text-indent — MDN。
- @supports CSS at-rule — MDN Web Docs。
- padding-inline-start CSS property — MDN Web Docs。
- white-space CSS property — MDN Web Docs。