引言
分页媒体对文档内容的显示有许多 特殊需求,这些需求随着印刷书籍的悠久历史而演进。运行页眉和页脚可作为导航辅助。注释可能会出现在页面底部,作为脚注。页面本身的属性可能会依据其内容或在文档中的位置而改变。引导线在视觉上连接相关内容。交叉引用可能需要生成的文本。某些分页媒体格式(例如 PDF)使用书签进行导航。
本模块定义了新属性和新取值,以便作者能够在分页媒体中使用这些技术。
值定义
本规范遵循来自 [CSS21] 的 CSS 属性定义约定,并使用来自 [CSS-VALUES-3] 的 值定义语法。本规范中未定义的值类型在 CSS Values & Units [CSS-VALUES-3] 中定义。与其他 CSS 模块的结合可能会扩展这些值类型的定义。
除了定义中列出的属性特定值外,本规范中定义的所有属性也都接受 CSS 全局关键字 作为其属性值。为了可读性,未明确重复列出。
1. 运行页眉和页脚
[CSS3PAGE] 描述了十六个可用于运行页眉和页脚的页面边距盒,但未描述向这些盒中插入内容的机制。本模块提供了两种实现方式。命名字符串可复制文本以在边距盒中重复使用。运行元素则将完整的元素(包括样式和结构)从文档移动到边距盒中。1.1. 命名字符串
string-set 属性会把元素的文本内容复制到一个 命名字符串 中,充当变量。可以使用 string() 函数来获取该命名字符串的文本内容。由于这些变量在同一页上可能会改变,string() 函数的可选第二个参数允许作者选择使用页面上的哪一个值。1.1.1. string-set 属性
| 名称 | string-set |
|---|---|
| 值 | [ <custom-ident> <content-list> ]# | none |
| 初始值 | none(无) |
| 应用于 | 所有元素,但不包括伪元素 |
| 可继承 | 否 |
| 百分比 | 不适用 |
| 计算值 | 指定的值 |
| 规范顺序 | 按语法 |
| 动画类型 | 离散 (discrete) |
用户代理应在所有介质(包括非可视介质)上支持此属性。
string-set 属性包含一个或多个 pair,每个 pair 由一个自定义标识符(即命名字符串的名称)后跟一个 <content-list>,该列表描述如何构造该命名字符串的值。
<content-list> 可扩展为以下一个或多个值,顺序任意。
<content-list> = [ <string> | <counter()> | <counters()> | <content()> | <attr()> ]+
- <string>
- 一个 字符串,定义见 [CSS21]
- <counter()>
- 一个 counter() 函数,定义见 [CSS21]。
- <counters()>
- 一个 counters() 函数,定义见 [CSS21]。
- <content()>
- The content() 函数,下面将作说明。
- <attr()>
- 一个 attr() 函数,定义见 [CSS-VALUES-3]
1.1.1.1. content() 函数
content() = content([text | before | after | first-letter ])
- text
- 元素的字符串值,等同于已设置
white-space: normal时的表现。这是默认值。 - before
::before伪元素的字符串值,等同于已设置white-space: normal时的表现。- after
::after伪元素的字符串值,等同于已设置white-space: normal时的表现。- first-letter
- 元素的首字母,按
::first-letter伪元素的定义。
命名字符串的内容值在元素的内容框首次创建(或如果元素的 display 为 none,则在本应创建时)时被赋值。页面的 入口值 是前一页面结束时生效的赋值。页面的 退出值 是当前页面结束时生效的赋值。
每当元素的值发生改变,命名字符串的值也会更新。用户代理必须能够记忆命名字符串的多个历史值,因为 string() 函数可以返回过去、当前或将来的赋值。
HTML
<h1>Loomings</h1>
CSS
h1::before { content: 'Chapter ' counter(chapter); }
h1 { string-set: header content(before) ':' content(text); }
h1::after { content: '.'; }
命名字符串 “header” 的值将是 “Chapter 1: Loomings”。
<section title="Loomings">
CSS
section { string-set: header attr(title) }
“header” 字符串的值将是 “Loomings”。
1.1.2. string() 函数
string() 函数用于通过 content 属性把命名字符串的值拷贝到文档中。该函数需要一个参数——命名字符串的名称。因为同一页上同一命名字符串的值可能会出现多次(随着定义该字符串的元素出现),可选的第二参数指明在出现多次赋值时应使用哪一个值。该函数的第二参数可以是以下关键字之一:
- first
- 使用页面上的首次赋值。如果页面上没有赋值,则使用 入口值。
first为默认值。 - start
- 如果元素是页面上的第一个元素,则使用首次赋值的值;否则使用 入口值。如果元素尚未出现,入口值 可能为空。
- last
- 使用命名字符串的 退出值。
- first-except
- 这与
first完全相同,只是当值在页面上被赋值时使用空字符串。
@page {
size: 15cm 10cm;
margin: 1.5cm;
@top-left {
content: "first: " string(heading, first);
}
@top-center {
content: "start: " string(heading, start);
}
@top-right {
content: "last: " string(heading, last);
}
}
h2 { string-set: heading content() }
下面的示例展示了 “heading” 字符串在不同页面上的 first、start、last 三种赋值。
h2 开头,start 值即为该标题的值。
h2,start 值取自前一页的 退出值。1.2. 运行元素
许多页眉和页脚不能仅用未格式化的文字表示。例如,书名作为运行页眉时可能包含斜体字。需要一种机制将元素整体(包括样式和结构)移动或复制到边距盒中。为此,我们把 running() 值添加到position 属性,并把 element() 值添加到 content 属性。@page {
@top { content: element(header); }
}
h1 { position: running(header); }
在本例中,h1 元素被放置在 @top 边距盒中,保持原有的格式以及所有子元素。它不再在普通文档流中显示。
1.2.1. running() 值
position: running(custom-ident) 会将元素(以及关联的 ::before 与 ::after 伪元素)从正常流中移除,并使其可通过 element() 放置到页面边距盒中。元素仍从文档中原始位置继承属性,但不在该位置显示。
| 名称 | position |
|---|---|
| 新值 | <running()> |
running() = running( <custom-ident> )
HTML
<p class="rh"><i>Miranda v. Arizona</i> in Context</p> <h2><i>Miranda v. Arizona</i> in Context</h2>
CSS
@top-center {
content: element(heading);
}
p.rh {
position: running(heading);
}
p.rh::before {
content: counter(page) ' / ';
}

元素的 value() 只能在页面边距盒中使用,且不能与 content 属性的其他可能取值组合使用。
如果我们还能 复制(而不是仅移动)元素,这一想法将更加实用。这样可以避免上例中 HTML 的重复。
Bert Bos 提出了替代语法,允许在运行页眉中既移动又复制元素。以下示例中,h2 元素仍保留在文档中的正常位置,同时也被复制到运行页眉中。
h2 {
display: block;
running: chapter;
font-size: 18pt;
font-weight: bold;
}
h2:running {
display: inline;
font-size: 11pt;
font-weight: normal;
font-variant: small-caps;
letter-spacing: 1pt;
}
@page {
@top-center {
content: element(chapter);
}
}
| 名称 | running |
|---|---|
| 值 | <custom-ident> |
| 初始值 | none(无) |
| 应用于 | 元素 |
| 可继承 | 否 |
| 百分比 | 不适用 |
| 计算值 | 指定的值 |
| 规范顺序 | 按语法 |
| 动画类型 | 离散 (discrete) |
用户代理应在所有介质(包括非可视介质)上支持此属性。
1.2.2. element() 值
element() 取值用于将已通过 running() 从正常流中移除的元素放入页面边距盒。当元素的值发生变化时,element() 的取值也会随之更新。
与 string() 类似,element() 也接受可选关键字,以描述在页面上出现多次赋值时应使用哪个值。用户代理必须能够记忆多个历史值,因为 element() 能返回过去、当前或将来的赋值。
| 名称 | 内容 |
|---|---|
| 新值 | <element()> |
element() = element( <custom-ident> , [ first | start | last | first-except ]? )
2. 脚注
辅助内容可能会被移动到页面的底部或侧边。当此类内容被移至页面底部并留下引用指示器时,就会产生脚注。2.1. 术语
脚注是复杂对象(见 脚注章节,来自 [dpub-latinreq]),因此在继续之前先定义一些术语会更有帮助。

- footnote element
- 包含脚注内容的元素,该元素会从文档流中移除并以脚注形式显示。
- footnote marker(亦称 footnote number)
- 位于脚注主体旁边的数字或符号,用以标识具体的脚注。脚注标记应使用与对应的 footnote call 相同的数字或符号,尽管标记里可以包含额外的标点符号。
- footnote body
- 脚注标记放在 footnote element 前面,两者共同构成 footnote body,随后会被放置到脚注区域中。
- footnote call(亦称 footnote reference)
- 出现在正文中的数字或符号,用来指向对应的脚注主体。
- footnote area
- 用于显示脚注的页面区域。
- footnote rule(亦称 footnote separator)
- 通常使用水平分隔线将脚注区域与页面其余部分分开。若页面没有脚注,则分隔线(以及整个脚注区域)不可渲染。
2.2. 创建脚注
将float: footnote 应用于元素即可使其成为脚注。这会触发以下操作:- 脚注元素从流中移除,并在其原位置插入一个
::footnote-call伪元素,作为脚注的引用。 - 在脚注元素开头放置一个
::footnote-marker伪元素,用以标识脚注。两者合起来构成脚注主体。 - 脚注计数器递增。
- 脚注主体被放置在页面底部的脚注区域中。属于同一页面的脚注元素按文档顺序放入该页的脚注区域。
<p>Though the body was erect, the head was thrown back so that the closed eyes were pointed towards the needle of the tell-tale that swung from a beam in the ceiling.<span class="footnote">The cabin-compass is called the tell-tale, because without going to the compass at the helm, the Captain, while below, can inform himself of the course of the ship.</span></p>
CSS
@page {
@footnote {
float: bottom;
}
}
span.footnote { float: footnote; }
为何在脚注区域使用 float:bottom?将脚注浮动到脚注区域,然后再将脚注区域本身浮动似乎过于复杂,因为实现通常不允许脚注区域在其它地方浮动。注意有的实现允许脚注区域使用绝对定位。
2.3. 脚注的类型
以下 float 属性的新取值会创建脚注元素:| 名称 | float |
|---|---|
| 新值 | footnote |
- footnote
- 每个脚注元素会被放置到该页面的脚注区域。
footnote-display 属性决定脚注是作为块级元素还是行内元素显示。
| 名称 | footnote-display |
|---|---|
| 值 | block | inline | compact |
| 初始值 | block |
| 应用于 | 元素 |
| 可继承 | 否 |
| 百分比 | 不适用 |
| 计算值 | 指定的值 |
| 规范顺序 | 按语法 |
| 动画类型 | 离散 (discrete) |
- block
- 脚注元素作为块级元素放置在脚注区域中。
- inline
- 脚注元素作为行内元素放置在脚注区域中。
- compact
- 用户代理自行决定将给定脚注元素作为块级还是行内放置。如果同一行可以容纳两个或更多脚注,则应使用行内布局。
2.4. 脚注区域
在 页面上下文 中使用@footnote 规则可以描述用于显示脚注的 页面区域。此规则定义的盒子(若使用)会包含该页面上出现的所有脚注元素。
2.4.1. 脚注区域的位置
脚注区域的底部外边距边缘定位到页面区域的底部。脚注区域只能包含脚注。在多列文本中脚注如何工作?Prince 使用 float: prince-column-footnote 在列底部而非页面底部创建脚注。
大多数实现支持类似 float: bottom 的页面浮动。页面浮动应位于脚注区域之上。该如何在规范中说明?
2.4.2. 脚注区域的大小
脚注区域的 max-height 属性限制该区域的尺寸,除非页面仅包含脚注(如文档最后一页的情况)。
由于页面只由脚注组成并非理想状态,用户代理 may 为脚注区域设置默认的 max‑height 值。
2.5. 脚注计数器
脚注计数器 是与 footnote element 关联的预定义 counter。它的值是用于标识脚注的数字或符号,并在 footnote call 与 footnote marker 中使用。每个脚注都应递增该计数器。
2.5.1. 脚注计数器的值
脚注计数器(如同其他计数器)可以使用任意 计数样式。脚注常使用一系列符号。
::footnote-call { content: counter(footnote, symbols('*', '†', '‡', '§')); }
::footnote-marker { content: counter(footnote, symbols('*', '†', '‡', '§')) '. '; }
2.5.2. 重置脚注计数器
脚注计数器可以在每页上重置。
请注意,脚注计数器的值应取决于脚注元素在文档树中的位置,而不是它最终被放置的位置。脚注元素有时会在脚注引用之后才出现在页面上,但两者必须使用相同的计数器值。
2.6. footnote-call 伪元素
当脚注元素被从流中移除时,会在其位置插入一个::footnote-call 伪元素,作为对脚注的引用。默认情况下,该伪元素的内容为脚注计数器的值,显示为上标数字。::footnote-call {
content: counter(footnote);
vertical-align: baseline;
font-size: 100%;
line-height: inherit;
font-variant-position: super;
}
2.7. footnote-marker 伪元素
::footnote-marker 伪元素表示脚注元素的标记(即标识每个脚注的数字或符号)。它的行为类似 ::marker 伪元素,定义见 [CSS3LIST]。该伪元素位于其父元素内容的开头,默认为行内元素。可以像其他 ::marker 元素一样进行样式化,默认样式应包括 list-style-position: inside。
2.8. 渲染脚注和 footnote policy
渲染脚注可能比较复杂。如果脚注出现在页面底部附近,页面可能没有足够空间容纳脚注主体。footnote-policy 属性让作者能够在难以布局的页面上施加一定的影响。
| 名称 | footnote-policy |
|---|---|
| 值 | auto | line | block |
| 初始值 | auto |
| 应用于 | 元素 |
| 可继承 | 否 |
| 百分比 | 不适用 |
| 计算值 | 指定的值 |
| 规范顺序 | 按语法 |
| 动画类型 | 离散 (discrete) |
- auto
- 用户代理自行决定如何渲染脚注,且可以将脚注主体放在脚注引用之后的后续页面上。脚注主体永远不能出现在脚注引用之前的页面。
- 行
- 如果当前页面因空间不足而无法放置某脚注主体,用户代理将在包含脚注引用的行开头强制分页,使引用和脚注主体一起出现在下一页。注意,此时用户代理必须遵守 孤行和寡行 设置,必要时可能需在更早的行上插入分页符。
- block
- 同 line,只是在脚注所在段落之前强制分页。
2.9. 未来方向
下一阶段将加入旁注、列脚注以及多个脚注区域等功能。3. 选择页面
一个分页文档由一系列页面组成。[CSS3PAGE] 定义了 页面选择器,用于选择文档的第一页、左页、右页以及空白页。这里我们在此基础上扩展,使其能够选择任意文档页面。
3.1. 页面选择器
:nth() 页面伪类允许选择任意文档页面。该伪类接受形如 An + B 的参数,定义见 [CSS3SYN]。当它应用于默认的 @page 规则时,:nth() 选中索引匹配该参数的文档页面。:nth() = :nth( <an+b> [of <custom-ident>]? )
:nth() 与页面计数器无关,页面计数器可能会重置并使用各种编号方案。
当 :nth() 选择器应用于已命名页面且该页面属于一个页面组(见下文)时,它选中该页面组中的第 n 页。
3.2. 页面组
许多分页文档具有重复结构,如多个章节、节或文章。每个子结构的第一页通常需要特殊处理,但 [CSS3PAGE] 并未定义选择每章第一页(不同于整个文档的第一页)的方法。当 page 属性与强制换页属性同时作用于同一元素时,会创建一个 页面组。页面组 包含该元素实例产生的所有页面。当渲染该元素的新实例时,将启动一个新的 页面组。
一个页面可以属于多个页面组,因为某个 页面组 的祖先或后代可能属于另一个 页面组。
div {
page: A;
break-before: page;
}
child {
page: B;
break-before: page;
}
@page :nth(5 of A) /* will select 5th page of every <div> */ @page :nth(1 of B) /* will select first page of every <child> */ @page :nth(5) /* will select 5th page of document */
考虑如下 HTML:
<div class="chapter"> <h1>Chapter One</h1> <p>some text</p> <table class="broadside"> ... </table> ... </div> <div class="chapter"> <h1>Chapter Two</h1> <p>some text</p> ... <table class="broadside"> ... </table> ... </div>
以及下面的 CSS:
div.chapter {
page: body;
break-before: page;
}
table.broadside {
page: broadside;
break-before: page;
}
在此情况下,每个章节都会形成一个独立的页面组。@page:nth(3 of body) 将选中每章的第三页,即便该页使用了 “broadside” 命名页面。@page:nth(1) 则只会选中文档的第一页,@page:first 亦如此。
4. 引导线(已移动)
现已在 [CSS3‑CONTENT] 中描述
5. 交叉引用(已移动)
现在已在 [CSS3-CONTENT] 中描述
6. 书签(已移动)
现在已在 [CSS3-CONTENT] 中描述
附录 A:它们现在在哪里?
许多在 2011 年 11 月 29 日工作草案 中的章节已被迁移到其他规范。以下是各章节迁移位置的说明。
页面标记和出血区
此节已移至 CSS Paged Media Module Level 3。
CMYK 颜色
此节已移至 CSS Color Module Level 5。
空白页的样式
此节已移至 CSS Paged Media Module Level 3
分页演示
此节已移至 CSS Overflow Module Level 4。
页面之间的导航
此内容在 WHATWG CSS Books 中讨论。
页面浮动
此节已移至 CSS Page Floats。
选择列和页面
关于选择列的简短说明可在 WHATWG CSS Books 中找到。
附录 B:默认 UA 样式表
本附录仅供参考,用于帮助 UA 开发者实现 HTML 的默认样式表,但 UA 开发者可根据需要自由忽略或修改。
@page {
counter-reset: footnote;
@footnote {
counter-increment: footnote;
float: bottom;
column-span: all;
height: auto;
}
}
::footnote-marker {
content: counter(footnote);
list-style-position: inside;
}
::footnote-marker::after {
content: '. ';
}
::footnote-call {
content: counter(footnote);
vertical-align: super;
font-size: 65%;
}
@supports ( font-variant-position: super ) {
::footnote-call {
content: counter(footnote);
vertical-align: baseline;
font-size: 100%;
line-height: inherit;
font-variant-position: super;
}
}
h1 { bookmark-level: 1 }
h2 { bookmark-level: 2 }
h3 { bookmark-level: 3 }
h4 { bookmark-level: 4 }
h5 { bookmark-level: 5 }
h6 { bookmark-level: 6 }
附录 C:更改
自 2014 年 5 月 13 日工作草案 以来的更改
- 澄清只有通过 bookmark-level 属性才能创建书签。去除 none 在 bookmark-label 中的取值。
- 将交叉引用、领袖(leaders)以及书签章节迁移至 CSS Generated Content Module。
- 错误的 break 取值已修正。参见 Issue #3524。
- 规范拥有了新的编辑。
自 2013 年 9 月 24 日编辑稿 以来的更改
- 规范拥有了新的编辑。
- 所有文本和示例均已重写。
- 为 string-set 属性添加了 attr(<identifier>) 取值。此特性已被 Prince 与 AntennaHouse 支持。
- 新增 footnote-policy 属性,用于在困难情况下控制脚注的渲染。
- 新增 footnote-display 属性,以允许内联脚注。
- 删除了侧注(sidenotes)章节。
- 删除了在页面和列内选择元素的章节。
- 删除了 page-group 属性,并为 nth() 页面伪类添加了可选参数,以允许在页面组内进行选择。
自 2011 年 11 月 29 日工作草案 以来的更改
- “页面标记和出血区”章节已迁移至 CSS Paged Media Module Level 3
- “CMYK 颜色”章节已迁移至 CSS Colors Level 5。
- 已删除“空白页的样式”章节。
- “分页演示”章节已迁移至 CSS Overflow Module Level 3。
- “页面之间的导航”已迁移至 WHATWG CSS Books
- “页面浮动”章节已迁移至 CSS Page Floats
- 已删除:first-page 伪元素章节。
- 已删除“选择列和页面”章节。
- 已从 string-set 属性中移除 env() 函数。
- 已移除 leaders 的对齐取值。
- leader 现在被指定仅占用单行。
- target-text 的 content() 取值已更改为 content(text)
- 已移除 content 属性的 target-pull() 取值。
与 WHATWG CSS Books 规范的差异
- 为 string-set 属性添加了 attr(<identifier>) 取值。此特性已被 Prince 与 AntennaHouse 支持。
- CSS Books 中没有 footnote-display 属性。
- CSS Books 中没有 footnote-policy 属性。
- 已阐明 nth() 页面伪类与 page-group 概念之间的关系,这可能导致 page-group 属性变得多余。
- 本规范并未重新定义 :first 页面伪选择器,以选择 page-group 的第一页。
- 本规范不考虑
- 命名区域
- 在页面内选择元素
- range() 页面伪类
- 选择列
- 基线节奏
- 扩展盒模型
- 字符替换
- 微排版
隐私考量
本规范未报告任何新的隐私考量。
安全考量
本规范未报告任何新的安全考量。
致谢
若没有 Håkon Wium Lie 的巨大贡献,此工作不可能完成。Chris Lilley、Elika J. Etemad、Alan Stearns、L. David Baron、Bert Bos、Florian Rivoal、[$your_name, ", " ]+ 与 Liam Quin 提供了宝贵的反馈。