若要为由 <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。

完成前检查换行和文本流

将页面调整到足以让两个较长条目都发生换行的宽度进行测试。然后扩大窗口,直到它们都能显示为单行。使用以下检查来区分缩进问题与换行或结构问题。

  1. 检查每个条目的起始位置。 在保留换行的示例中,每个 <br> 后的文本都应回到外侧边缘。如果没有,请检查浏览器是否支持完整的关键字组合。
  2. 检查每个续行。 较长条目应向内缩进。较短条目应保持齐头,不带额外前导空格。
  3. 检查预期使用的最窄栏宽。 如果缩进占用了太多可用行宽,请减小缩进。
  4. 检查实际内容。 将内容移入独立块时,保留每个条目的文字、行内强调格式以及预期的显式换行。

如果没有任何内容换行,请检查 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;对于独立条目,则为每个条目设置自己的块。确认每个条目都齐头开始,并且只有自然换行的续行会在预期使用的宽度下向内缩进。

来源