1. 简介
CSS 定义了一组有限的参数,称为 属性,用于指导文档的渲染。 每个 属性 都有一个名称 (例如,color、font-size 或 border-style), 一个值空间 (例如,<color>、 <length-percentage>、[ solid | dashed | dotted | … ]), 以及对文档渲染的定义行为。 属性值通过 属性声明 赋给文档的各个部分, 为关联的元素或框赋值 (例如 red、12pt、dotted)。
CSS 的基本设计原则之一是 层叠, 允许多个样式表影响文档的展示。 当不同的声明尝试为同一元素/属性组合设置值时, 必须以某种方式解决冲突。
相反的问题在于,当没有声明尝试为元素/属性组合设置值时, 可以通过 继承 或查看属性的 初始值 来找到一个值。
层叠 和 默认化 过程 将一组声明作为输入, 输出每个元素上每个属性的 指定值。
本规范描述了在文档中为所有元素上的所有属性查找指定值的规则。 查找页面上下文及其页边框中的指定值的规则在 [css-page-3] 中描述。
1.1. 模块交互
本节是规范性的。
本模块替换并扩展了 [CSS2] 第 6 章中定义的属性值赋值、层叠和继承的规则。
其他 CSS 模块可能会扩展此处定义的某些语法和功能的定义。 例如,媒体查询第 4 级规范, 与本模块结合时,扩展了 <media-query> 值类型在本规范中的定义。
为了本规范的目的,文本节点 被视为其关联元素的元素子节点,并拥有完整的属性集; 由于它们不能被选择器定位, 所有它们的计算值都由 默认化 赋值。
2. 导入样式表:@import 规则
@import 规则允许用户从其他样式表中导入样式规则。 如果 @import 规则引用了一个有效的样式表, 用户代理必须将该样式表的内容视为在 @import 规则的位置处编写,除以下两种情况外:
-
如果某个特性(如 @namespace 规则)明确 定义它只适用于特定的样式表,而不适用于任何导入的样式表, 那么它不适用于导入的样式表。
-
如果某个特性依赖于样式表中两个或多个构造的相对顺序 (例如要求 @namespace 规则之前不能有任何其他规则,除了 @import 规则), 那么它仅适用于同一个样式表中的构造之间。
例如,导入的样式表中的样式规则中的声明与层叠的交互方式与它们在 @import 位置处编写时相同。
任何 @import 规则必须位于样式表中所有其他有效的 at 规则和样式规则之前 (忽略 @charset 和空的 @layer 定义), 并且在它与之前的 @import 规则之间不能有任何其他有效的 at 规则或样式规则, 否则 @import 规则无效。 @import 的语法如下:
@import [ <url> | <string> ] [ layer | layer(<layer-name>) ]? [ supports( [ <supports-condition> | <declaration> ] ) ]? <media-query-list>? ;
其中:
-
可选的 layer 关键字或 layer() 函数将样式表内容分配到其自身的匿名 层叠层 或命名的 层叠层 中。
即使导入失败,层仍会添加到 层顺序 中, 但受任何 导入条件 的限制(就像由 @layer 规则在适当的 条件组规则中声明一样)。
-
可选的 [<supports-condition>|<declaration>] 和 <media-query-list>(统称为 导入条件) 声明其适用的条件。
@import url("narrow.css") supports(display: flex) handheld and (max-width: 400px);
@import url("tabs.css") layer(framework.component); @import url("override.css") layer();
如果提供了 <string>, 则必须将其解释为具有相同值的 <url>。
@import "mystyle.css" ; @import url ( "mystyle.css" );
2.1. 条件 @import 规则
导入条件 允许导入样式表根据媒体或特性支持。 如果没有任何 导入条件,则导入是无条件的。 (为 <media-query-list> 指定 all 具有相同的效果。) 如果 导入条件 不匹配, 则导入的样式表中的规则不适用, 就像导入的样式表被包裹在 @media 和/或 @supports 块中一样。
@import url ( "fineprint.css" ) print; @import url ( "bluish.css" ) projection, tv; @import url ( "narrow.css" ) handheld and( max-width:400 px );
因此,只要 导入条件 不匹配, 用户代理可以避免获取条件导入。 此外,如果 <supports-condition> 阻止了导入的样式表的应用, 用户代理 不得 获取样式表(除非它通过其他链接加载), 并且 必须 返回 null 作为导入规则的 CSSImportRule.styleSheet 值 (即使它通过其他链接加载)。
@import url ( "fallback-layout.css" ) supports ( not( display: flex)); @supports ( display: flex) { ...}
导入条件 由 <media-query-list> 给出,并被解析和解释为 媒体查询列表, 以及 <supports-condition>,解析和解释为 [[supports query]]。 如果提供了 <declaration> 代替 <supports-condition>, 则必须将其解释为 <supports-decl>(即,额外的括号是隐含的), 并视为 <supports-condition>。
@import "mystyle.css" supports ( display: flex); @import "mystyle.css" supports (( display: flex));
导入条件 的评估和完整语法由 媒体查询 [MEDIAQ] 和 CSS 条件规则 [CSS-CONDITIONAL-3] 规范定义。
2.2. 样式表导入处理
当同一个样式表在多个地方被导入或链接到文档时, 用户代理必须处理(或表现为如此)每个链接,就像链接到一个独立的样式表一样。
注意: 这不对资源的获取方式提出任何要求, 只影响样式表在 CSSOM 中的反映方式以及在此类规范中的使用。 假设适当的缓存, 用户代理完全可以只获取样式表一次, 即使它被多次链接或导入。
导入的样式表的层叠来源 是导入它的样式表的 层叠来源。
导入样式表的环境编码 是导入它的样式表的编码。 [css-syntax-3]
2.3. CSS 样式表的内容类型
导入的样式表的处理依赖于链接资源的实际类型:
-
如果资源没有 内容类型元数据, 则将其类型视为
text/css
。 -
如果宿主文档处于 怪异模式, 且宿主文档的来源与链接资源 响应的 URL 的来源相同, 则将其类型视为
text/css
。 -
否则,其类型由其 内容类型元数据 决定。
如果链接资源的类型是 text/css
,
则必须将其解释为 CSS 样式表。
否则,必须将其解释为网络错误。
3. 简写属性
某些属性是 简写属性, 意味着它们允许作者使用单个属性来指定多个属性的值。 简写属性会设置其所有的 长写子属性, 就像在原地展开一样。
当简写形式中省略某些值时, 除非另有定义, 每个“缺失”的 子属性都会被分配其 初始值。
例如,编写 background: green 而不是 background-color: green 可以确保背景颜色覆盖任何可能设置了背景图像的早期声明 background-image。
h1{ font-weight : bold; font-size : 12 pt ; line-height : 14 pt ; font-family : Helvetica; font-variant : normal; font-style : normal; }
因此可以重写为
h1{ font : bold12 pt /14 pt Helvetica}
在某些情况下,简写 属性可能具有不同的语法或特殊关键字, 而这些关键字并不直接对应于其 子属性 的值。 (在这种情况下,简写属性会明确定义其值的展开方式。)
在其他情况下,某个属性可能是简写属性的 仅重置子属性: 像其他 子属性 一样,未指定时它会被简写重置为其初始值, 但简写可能没有语法来设置 子属性 为任何其他值。例如,border 简写会将 border-image 重置为其初始值 none, 但没有语法将其设置为其他值。[css-backgrounds-3]
如果将 简写 属性指定为 CSS 宽泛关键字 [css-values-3], 则它将所有 子属性 设置为该关键字, 包括任何 仅重置子属性。 (请注意,这些关键字不能与单个声明中的其他值组合,即使是在简写中也不行。)
将 简写 属性声明为 !important 等同于将其所有的 子属性 声明为 !important。
3.1. 属性别名
有时,属性在被支持了一段时间后会更改名称,例如供应商前缀属性被标准化。 出于兼容性原因,仍需要支持原始名称,但新名称是首选的。 为此,CSS 定义了两种将旧语法“别名化”到新语法的方法。
- 旧名称别名
-
当旧属性的值语法与新属性的值语法相同时,
两个名称通过类似于大小写映射的操作进行别名化:
在解析时,将旧属性转换为新属性。
此转换也适用于 CSSOM,
无论是字符串参数还是属性访问器:
对旧属性名的请求透明地转移到新属性名。
例如,如果 old-name 是 旧名称别名 对于 new-name,
getComputedStyle
将返回( el). oldNamenewName
属性的计算样式, 并且el
将把 new-name 属性设置为. style. setPropertyValue( "old-name" , "value" )
。"value" - 旧简写
-
当旧属性的语法与新属性的语法不同,
两个名称通过 简写
机制进行别名化。
这些简写被定义为 旧简写,其使用是不推荐的。
它们的行为与常规简写完全相同,
唯一不同的是,在序列化声明时 CSSOM 不会使用它们。[CSSOM]
例如,page-break-* 属性 是 旧简写 对于 break-* 属性 (参见 CSS Fragmentation 3 § 3.4 页面断点别名:page-break-before、page-break-after 和 page-break-inside 属性)。
设置 page-break-before: always 在解析时展开为 break-before: page, 类似于其他简写。 同样地,如果设置了 break-before: page, 调用
getComputedStyle
将返回( el). pageBreakBefore
。 然而,在序列化样式块时 (参见 CSSOM 1 § 6.7.2 序列化 CSS 值), 永远不会选择 page-break-before 作为序列化的简写, 无论是它还是 break-before 被指定; 而是总是选择 break-before。"always"
3.2. 重置所有属性:all 属性
名称: | all |
---|---|
值: | initial | inherit | unset | revert | revert-layer |
初始值: | 见各个属性 |
适用: | 见各个属性 |
是否继承: | 见各个属性 |
百分比: | 见各个属性 |
计算值: | 见各个属性 |
动画类型: | 见各个属性 |
规范顺序: | 按语法 |
all 属性是一种 简写,用于重置所有 CSS 属性, 除了 direction 和 unicode-bidi。 它只接受 CSS 宽泛关键字。 它不会重置 自定义属性 [css-variables-1]。
注意: 被排除的 CSS 属性 direction 和 unicode-bidi 实际上是标记级特性,
不应在作者的样式表中设置。
(它们作为 CSS 属性存在仅是为了样式化用户代理不支持的文档语言。)
作者应使用适当的标记,例如 HTML 的 dir
属性。[css-writing-modes-3]
这对于页面中包含的“小部件”的根元素非常有用,
该元素不希望继承外部页面的样式。
但是请注意,应用于该元素的任何“默认”样式
(例如,用户代理样式表对 <div>
这样的块级元素应用的 display:
block)
也将被清除。
4. 值处理
一旦用户代理解析了文档并构建了文档树,它必须为树中的每个元素,以及对应的格式化结构中的每个框,分配适用于目标媒体类型的每个属性的值。
给定元素或框的 CSS 属性的最终值是多步计算的结果:
- 首先,收集应用于元素的每个属性的所有声明值。 每个元素上可能有零个或多个声明值。
- 层叠确定层叠值。 每个元素的每个属性最多有一个层叠值。
- 默认值确定指定值。 每个元素的每个属性有且仅有一个指定值。
- 解决值依赖关系后得出计算值。 每个元素的每个属性有且仅有一个计算值。
- 格式化文档后得出使用值。 只有当属性适用于该元素时,元素才有该属性的使用值。
- 最后,使用值根据显示环境的约束转换为实际值。 与使用值一样,可能会或不会存在某个属性的实际值。
未连接或不属于文档扁平化元素树的一部分的元素不参与 CSS 值处理,
因此没有声明值、层叠值、指定值、计算值、使用值或实际值,
即使它们可能有样式声明(例如通过style
属性)赋予它们。
4.1. 声明值
应用于元素的每个属性声明应用于元素为该属性提供了一个声明值。 详情请参见过滤声明。
这些值然后通过层叠处理,以选择一个“获胜值”。
4.1.1. 值别名
一些属性值具有旧版值别名: 在解析时,旧版语法被转换为新语法, 结果是一个与解析输入不同的声明值。 这些别名通常用于处理旧版兼容性要求, 例如将供应商前缀值转换为它们的标准等价物。
4.2. 层叠值
层叠值表示层叠的结果: 它是赢得层叠的声明值 (在层叠输出中排序第一)。 如果层叠输出为空列表, 则没有层叠值。
4.3. 指定值
指定值是 样式表作者为该元素指定的给定属性的值。 它是将层叠值通过默认化过程后的结果, 保证每个元素的每个属性都有一个指定值。
在许多情况下,指定值就是层叠值。 但是,如果没有层叠值, 则指定值将被默认化。 当某些属性的层叠值为CSS-wide 关键字时, 它们会被特别处理, 设置为该关键字要求的指定值,详见§ 7.3 显式默认化。
4.4. 计算值
计算值是 解析指定值的结果,如属性定义表中的“计算值”行所定义, 通常是在继承之前将其绝对化。
注意: 计算值是继承期间从父元素传递到子元素的值。
由于历史原因,
它不一定是getComputedStyle()
函数返回的值,
该函数有时会返回使用值。
[CSSOM] 此外,计算值是抽象的数据表示形式:
它们的定义反映了这种数据表示形式,
而不是数据的序列化方式。
例如,序列化规则通常允许在解析时省略某些隐含的值;
但这些值仍然是计算值的一部分。
- 具有相对单位的值 (em、ex、vh、vw) 必须通过乘以适当的参考尺寸来绝对化
- 某些关键字 (例如,smaller,bolder) 必须根据其定义进行替换
- 某些属性上的百分比必须乘以参考值 (由属性定义)
- 有效的相对 URL 必须解析为绝对 URL。
参见下表中的示例 (f)、(g) 和 (h) 在下表中。
注意: 通常来说,计算值尽可能解析指定值,而不进行文档布局 或其他昂贵且难以并行化的操作, 例如解析网络请求 或检索除该元素及其父元素之外的值。
计算值即使在属性不适用时也依然存在。 但是,某些属性可能会根据属性是否适用于元素来更改它们确定计算值的方式。
4.5. 使用值
使用值是 通过对计算值进行所有剩余的计算所得的结果,以使其成为用于格式化文档的绝对理论值。
例如, width: auto 的声明 在不知道元素祖先的布局的情况下无法解析为一个长度值, 因此计算值是 auto, 而使用值是一个绝对长度,例如 100px。[CSS2]
另一个例子,一个
<div>
可能有一个break-before的计算值为
auto,
但通过从其第一个子元素中传播,获得一个使用的break-before值为
page。[css-break-3]
如果某个属性不适用于该元素或框类型, 那么它对于该属性没有使用值。
例如, flex 属性在不是 弹性项目的元素上没有 使用值。
4.5.1. 适用的属性
如果某个属性不适用于某个元素或框类型—
注意: 一个不适用的属性 仍然可能通过间接的方式影响格式化, 如果它的计算值影响到其他适用于该类型的属性的计算; 并且当然它的计算值, 无论如何存在, 仍然可以继承给后代元素 并对它们生效。
p
元素上
(默认情况下它是 display:
block)
会生效,
即使 text-transform 仅适用于
行内框,
因为该属性会继承到段落的匿名
根行内框 并应用于其中包含的文本。
注意: 适用于“所有元素”的属性适用于所有元素和 display 类型, 但不一定适用于所有 伪元素 类型, 因为伪元素通常有它们自己特定的渲染模型 或其他限制。 然而,::before 和 ::after 伪元素被定义为几乎与正常元素生成的框相同, 因此被定义为接受所有适用于“所有元素”的属性。 参见[CSS-PSEUDO-4]了解更多关于 伪元素的信息。
4.6. 实际值
使用值原则上已准备好使用, 但用户代理在特定环境中可能无法使用该值。例如,用户代理可能只能以整数像素宽度渲染边框, 因此可能需要对使用的宽度进行近似。 此外,元素的字体大小可能需要根据字体的可用性或 font-size-adjust属性的值进行调整。 实际值是 在进行这些调整后得到的使用值。
注意: 通过探查元素的实际值,可以学到很多关于文档布局的信息。 然而,并非所有信息都记录在实际值中。 例如,page-break-after属性的实际值 并不反映元素之后是否有分页符。 同样,orphans的实际值 也不反映某个元素中的孤行数量。 参见下表中的示例 (j) 和 (k) 在下表中。
4.7. 示例
属性 | 最终声明 | 层叠值 | 指定值 | 计算值 | 使用值 | 实际值 | |
---|---|---|---|---|---|---|---|
(a) | text-align | text-align: left
| left | left | left | left | left |
(b) | border-top-width, border-right-width, border-bottom-width, border-left-width | border-width: inherit
| inherit | 4.2px | 4.2px | 4.2px | 4px |
(c) | width | (无) | (无) | auto (初始值) | auto | 120px | 120px |
(d) | list-style-position | list-style-position: inherit
| inherit | inside | inside | inside | inside |
(e) | list-style-position | list-style-position: initial
| initial | outside (初始值) | outside | outside | outside |
(f) | font-size | font-size: 1.2em
| 1.2em | 1.2em | 14.1px | 14.1px | 14px |
(g) | width | width: 80%
| 80% | 80% | 80% | 354.2px | 354px |
(h) | width | width: auto
| auto | auto | auto | 134px | 134px |
(i) | height | height: auto
| auto | auto | auto | 176px | 176px |
(j) | page-break-after | (无) | (无) | auto (初始值) | auto | auto | auto |
(k) | orphans | orphans: 3
| 3 | 3 | 3 | 3 | 3 |
4.8. 按片段值处理
某些 CSS 特性可能会在每个片段的基础上干扰值的处理。 例如,参见 CSS Pseudo-Elements 4 § 2.1.3 Inheritance and the ::first-line Pseudo-element, 它改变了 ::first-line 伪元素中的片段的继承。 在这种情况下,个别片段被赋予了不同的指定值, 任何基于其他属性的计算值解析的值 (例如 currentcolor 或 em 单位) 都会在每个框片段中进行解析。 在每个片段中,后续的值处理正常进行。
假设每个框(而不是每个框片段)具有单一值的 API
必须忽略非树遵循的伪元素的影响。
(例如,::first-line 样式对
getComputedStyle()
返回的值没有影响。)
< div >< span > First line< br /> Second line</ span ></ div > < div >< span > First line</ span ></ div > < div > First line< br >< span > Second line</ span ></ div > < style > div { color : blue ; } div :: first-line { color : yellow ; } span { border : thin solid currentcolor ; } </ style >
在每个
div
中,
“First line” 文本是黄色,“Second line” 文本是蓝色;
包裹每一行的
span
的每个片段的边框与该颜色相匹配。
但是,对所有三个 span 执行 getComputedStyle()
,
返回的 border-color 的值都是 "blue"
,
因为 ::first-line 伪元素的影响对于不支持片段的 API 被忽略。
5. 过滤
为了找到声明的值, 实现必须首先识别所有适用于每个元素的声明。 如果声明符合以下条件之一,则适用于某个元素:
- 它属于当前适用于此文档的样式表。
- 它未被条件规则[CSS-CONDITIONAL-3]中为假的条件限定。
- 它属于样式规则,其选择器与该元素匹配。[SELECT](如有必要,考虑作用域。)
- 它在语法上是有效的: 声明的属性是已知的属性名, 且声明的值与该属性的语法匹配。
适用的声明值形成了, 对于每个元素的每个属性, 一个声明值列表。 下一节, 层叠, 对这些列表进行优先级排序。
6. 层叠
层叠对给定元素的给定属性的无序 声明值列表, 根据下文确定的声明优先级进行排序, 并输出一个层叠值。
6.1. 层叠排序顺序
层叠根据以下标准对声明进行排序,按优先级从高到低排列:
- 来源和重要性
-
声明的来源基于它的来源位置,
其重要性是指是否使用了 !important 声明(参见下文)。
不同来源的优先级按以下顺序降序排列:
- 过渡声明 [css-transitions-1]
- 重要的 用户代理 声明
- 重要的 用户 声明
- 重要的 作者 声明
- 动画声明 [css-animations-1]
- 普通的 作者 声明
- 普通的 用户 声明
- 普通的 用户代理 声明
来自此列表中较早的来源的声明优先于较晚的来源的声明。
- 上下文
-
文档语言可以提供从不同的
封装上下文中混合声明,
例如在 [DOM] 中
影子树的嵌套
树上下文。
当比较来自不同封装上下文的两个声明时, 对于普通的规则, 外部上下文的声明胜出; 对于重要的规则, 内部上下文的声明胜出。 为此目的,[DOM] 树上下文 被认为在 影子包括树顺序中嵌套。
注意: 这实际上意味着属于某个 封装上下文的 普通的声明可以设置很容易被外部上下文覆盖的默认值, 而属于封装上下文的重要的声明则可以执行无法被外部上下文覆盖的要求。
- 元素附加样式
-
分别对普通的和重要的声明,
直接附加到元素的声明(例如style 属性的内容)
相较于通过样式规则选择器间接映射的声明,具有更高的优先级。
注意: 非 CSS 表现性提示(例如表现性标记)是独立处理的, 参见§ 6.5 非 CSS 表现性提示的优先级。
- 层
-
在每个来源和上下文内的声明
可以显式地分配给层叠层。
在此步骤中,任何未分配到显式层的声明会被添加到隐式的最终层中。
层叠层(与声明一样)按出现顺序排列。 当比较属于不同层的声明时, 对于普通的规则,最后的层叠层中的声明胜出, 而对于重要的规则,首先的层叠层中的声明胜出。
注意: 这遵循了用于层叠普通的和重要的 来源的相同逻辑, 因此 !important 标志在这两种情况下都保持了相同的“覆盖”用途。
- 特异性
- 选择器模块 [SELECT]描述了如何计算选择器的特异性。 每个声明的特异性与它所在的样式规则相同。 特异性最高的声明优先。
- 出现顺序
-
文档顺序中最后的声明胜出。
为此目的:
- 样式表的顺序与最终 CSS 样式表中的顺序相同。
- 从导入的样式表的声明,按样式表替代 @import 规则的位置的顺序排列。
- 由源文档独立链接的样式表中的声明被视为按照主机文档语言确定的链接顺序连接在一起。
- 来自 style 属性的声明按照包含 style 属性的元素的文档顺序排列, 并且都位于任何样式表之后。
层叠输出是每个元素的每个属性的(可能为空的)声明值的排序列表。
6.2. 层叠来源
每个样式规则都有一个层叠来源, 它决定了规则在层叠中的位置。 CSS 定义了三个核心的来源:
- 作者来源
- 作者根据文档语言的约定为源文档指定样式表。 例如,在 HTML 中, 样式表可以包含在文档中或外部链接。
- 用户来源
- 用户可以为特定文档指定样式信息。 例如,用户可以指定包含样式表的文件, 或用户代理可以提供生成用户样式表的接口 (或表现得像是生成了样式表)。
- 用户代理来源
- 合格的用户代理必须应用默认样式表 (或表现得像是应用了样式表)。 用户代理的默认样式表应以符合文档语言的一般表现预期的方式呈现文档语言的元素 (例如,对于可视浏览器,HTML 中的 EM 元素使用斜体字体呈现)。 参见例如 HTML 用户代理样式表。[HTML]
CSS 的扩展定义了以下附加的来源:
- 动画来源
- CSS 动画 [css-animations-1] 在运行时生成表示其效果的“虚拟”规则。
- 过渡来源
- 类似于 CSS 动画,CSS 过渡 [css-transitions-1] 在运行时生成表示其效果的“虚拟”规则。
6.3. 重要声明:!important 注解
CSS 尝试在作者和用户样式表之间创造权衡。 默认情况下,作者样式表中的规则会覆盖用户样式表中的规则, 而用户样式表会覆盖用户代理默认样式表中的规则。 为了平衡这一点,可以将声明标记为重要, 这会增加它在层叠中的权重,并倒置优先顺序。
如果声明的值中最后两个(非空格,非注释)符号是分隔符 ! 和标识符 important, 则该声明是重要的, 如[css-syntax-3] 所定义。 所有其他声明都是普通的(非重要的)。
重要的声明优先于普通的声明。 作者和用户样式表中可以包含重要的声明, 其中用户来源的重要的声明 会覆盖作者来源的重要的声明。 这种 CSS 特性通过给予有特殊需求的用户 (大字体、颜色组合等) 对表现的控制,提高了文档的可访问性。
来自所有来源的重要的声明优先于动画。 这允许作者在重要的情况下覆盖动画值。 (动画值通常覆盖所有其他规则。)[css-animations-1]
用户代理样式表中也可以包含重要的声明。 这些声明会覆盖所有作者和用户声明。
/* 来自用户样式表 */ p{ text-indent : 1 em !important} p{ font-style : italic !important} p{ font-size : 18 pt } /* 来自作者样式表 */ p{ text-indent : 1.5 em !important} p{ font : normal12 pt sans-serif !important} p{ font-size : 24 pt }
属性 | 获胜值 |
---|---|
text-indent | 1em |
font-style | italic |
font-size | 12pt |
font-family | sans-serif |
6.4. 层叠层
就像层叠来源在用户和作者样式之间提供了平衡, 层叠层提供了一种结构化的方法, 用于在单一来源中组织和平衡各种需求。 单个层叠层中的规则彼此叠加, 而不会与层外的样式规则交织。
作者可以创建层来表示元素的默认值、
第三方库、主题、组件、
覆盖和其他样式需求,
audio{ /* specificity of 0,0,1 - implicit (final) layer */ display: flex; } @layer reset{ audio[ controls] { /* specificity of 0,1,1 - explicit "reset" layer */ display: block; } }
未分层的声明在
audio
元素上优先于显式分层的 audio[controls]
声明 —
定义名称的at 规则,例如 @keyframes 或 @font-face,在解决名称冲突时也会使用层的顺序。
/* establish the layer order, so the "override" layer takes precedence */ @layer framework, override; @layer override{ @keyframes slide-left{ from{ translate : 0 ; } to{ translate : -100 % 0 ; } } } @layer framework{ @keyframes slide-left{ from{ margin-left : 0 ; } to{ margin-left : -100 % ; } } } .sidebar{ animation : slide-left300 ms ; }
在这种情况下,override 层的层叠优先级高于 framework 层,
因此 slide-left
动画将使用 translate
属性而不是 margin-left
。
6.4.1. 声明层叠层
层叠层可以通过以下方式声明:
-
使用带有 layer 关键字或 layer() 函数的@import 规则, 将导入文件的内容分配到该层中。
-
使用@layer 块 at 规则, 将其子样式规则分配到该层中。
-
使用@layer 语句 at 规则, 声明一个不包含任何规则的命名层。
提供用于将 link 或 style 元素分配给层叠层的属性?[Issue #w3c/csswg-drafts#5853]
6.4.2. 层命名和嵌套
一个层叠层有一个层名称, 它是一个表示每个层嵌套级别的有序列表, 其中每个部分都可以命名(作为CSS 标识符)或匿名。 (因此,当一个层嵌套在另一个层中时, 其名称会连接在一起。) 当一个层在另一个层的范围内声明时, 它就是嵌套的, 例如,一个 @layer 规则在另一个 @layer 内, 一个嵌套导入中的@import, 或者一个嵌套导入中的 @layer 规则。
层名称如果包含相同顺序的相同部分, 则表示相同的层叠层; 然而,匿名部分在每次出现时都有唯一的身份。 请注意,嵌套可能导致多个层共享相同的匿名部分。
headings.css
和 links.css
的内容与 audio[controls]
规则在同一层内进行层叠:
@import url ( headings.css ) layer ( default); @import url ( links.css ) layer ( default); @layer default{ audio[ controls] { display : block; } }
@layer base{ p{ max-width : 70 ch ; } } @layer framework{ @layer base{ p{ margin-block : 0.75 em ; } } @layer theme{ p{ color : #222; } } }
生成的层可以表示为树结构:
-
base
-
framework
-
base
-
theme
-
或者表示为具有嵌套标识符的扁平列表:
-
base
-
framework.base
-
framework.theme
在语法上,显式的层名称由<layer-name>表示, 该名称用于@layer 和 @import 规则中, 它是由<ident>标记组成的以点分隔的列表, 中间没有空格:
<layer-name> = <ident> [ '.' <ident> ]*
CSS 宽范围关键字保留供将来使用, 如果在<ident>中使用, 会导致规则在解析时无效。 当多个标识符用点连接时, 这是代表这些层按顺序嵌套的简写形式。
@layer framework{ @layer default{ p{ margin-block : 0.75 em ; } } @layer theme{ p{ color : #222; } } } @layer framework.theme{ /* 这些样式将添加到 framework 层中的 theme 层中 */ blockquote{ color : rebeccapurple; } }
注意:嵌套层不能“逃逸”其父层来引用其外部的层。
6.4.2.1. 匿名层
当 @layer 规则省略其<layer-name>, 或者 @import 规则使用 layer 关键字(没有提供 <layer-name>), 其层名称获得唯一的匿名段; 因此它不能从外部引用。
-
多个未命名的层规则 将其样式放入不同的层中, 因为每次出现都引用不同的匿名层名称。
@layer { /* layer 1 */ } @layer { /* layer 2 */ } -
在单个未命名层中, 具有相同名称的子层引用相同的层叠层, 因为它们共享相同的匿名父层。
@layer { @layer foo{ /* layer 1 */ } @layer foo{ /* also layer 1 */ } } -
而在不同的未命名层中, 具有相同名称的子层引用不同的层叠层, 因为它们具有不同的匿名父层。
@layer { @layer foo{ /* layer 1 */ } } @layer { @layer foo{ /* layer 2 */ } }
这可能只是为了简洁方便, 也可以作为团队强制组织约定的一种方式 (该层中的所有代码必须在同一个地方定义), 或者由希望合并并隐藏一组内部“私有”层的库使用, 这些层不希望向作者公开进行操作:
/* bootstrap-base.css */ /* 未命名的包装层包围每个子文件 */ @import url ( base-forms.css ) layer; @import url ( base-links.css ) layer; @import url ( base-headings.css ) layer;
/* bootstrap.css */ /* 内部名称被隐藏无法访问,包含在“base”中 */ @import url ( bootstrap-base.css ) layer ( base);
/* author.css */ /* 作者可以访问 bootstrap.base 层,但无法进入未命名的层 */ @import url ( bootstrap.css ) layer ( bootstrap); /* 向 bootstrap 层添加额外的样式: */ @layer bootstrap{ ...}
6.4.3. 层顺序
层叠层按照首次声明的顺序进行排序, 嵌套的层在其父层内分组, 排在未分层规则之前。
/* 未分层样式在层顺序中排在最后 */ h1{ color : darkslateblue; } @layer reset.type{ strong{ font-weight : bold; } } @layer framework{ .title{ font-weight : 100 ; } @layer theme{ h1, h2{ color : maroon; } } } @layer reset{ [ hidden] { display : none; } }
首先对外层进行排序, 任何未分层的样式规则 被添加到一个隐含的外层中, 其优先级高于(在后面)显式层:
-
reset
-
framework
-
(隐含外层)
在每个层内, 嵌套的层按照出现顺序排序, 没有进一步嵌套的样式规则 同样被添加到显式嵌套层之后的隐含子层中:
-
reset.type
-
reset(隐含子层)
-
framework.theme
-
framework(隐含子层)
-
(隐含外层)
在条件组规则内定义的层不会影响层顺序, 除非条件为真 或除非条件组规则在文档中针对不同元素可以有不同的评估结果。
注意:由于层顺序在全局范围内适用, 在元素敏感的条件组规则内定义的任何层在建立全局层顺序时需要被考虑, 无论规则的条件如何。 然而,文档全局的条件 (如 @media 和 @supports) 可以根据条件处理这样的@layer规则。
@media ( min-width:30 em ) { @layer layout{ .title{ font-size : x-large; } } } @media ( prefers-color-scheme: dark) { @layer theme{ .title{ color : white; } } } @layer theme, layout;
如果第一个媒体查询基于视口尺寸匹配, 则layout层将首先出现在层顺序中。 如果颜色方案偏好查询匹配, 或者两个条件都不成立, 则 theme 将首先出现在层顺序中。
希望避免此行为的作者可以提前建立 层的显式顺序, 并避免在条件规则中定义新层。
注意:层叠层限定于其来源和上下文, 因此光 DOM 中层的顺序不会对 阴影 DOM 中同名层的顺序产生影响 (反之亦然)。
允许作者在层顺序中显式放置未分层样式 [Issue #6323]
6.4.4. 内联声明层:@layer规则
@layer规则 声明了一个层叠层, 并可以选择分配样式规则。
6.4.4.1. 内联分配样式:@layer块规则
@layer 块规则将其子样式规则分配给特定的命名层叠层。 这种块层分配语法为:
@layer <layer-name>? { <stylesheet> }
这样的@layer块规则与条件为真的条件组规则[CSS-CONDITIONAL-3]具有相同的限制和处理。
@layer framework{ h1, h2{ color : maroon; background : white;} @media ( prefers-color-scheme: dark) { h1, h2{ color : red; background : black; } } }
注意:@layer块规则 不能与@import规则交错使用。
6.4.4.2. 无样式声明:@layer声明规则
@layer 规则也可以用于定义新层,而不分配任何样式规则,仅提供层名称:
@layer <layer-name>#;
这样的空@layer规则可以在@import和@namespace规则之前(如果有的话,在@charset规则之后), 也可以在允许使用@layer块规则的任何地方使用。
注意:在@import和@namespace规则之间不允许有 @layer规则。 在@import或@namespace规则 之后出现的任何@layer规则将导致忽略其后的所有 @import或@namespace规则。
与块语法不同, 在此语法中可以提供多个逗号分隔的层名称, 按指定顺序声明每个层。
注意:由于层的排序由层名称的首次出现定义 (参见§ 6.4.3 层排序), 此规则允许页面提前声明层的顺序, 以便在不必阅读整个样式表的情况下显现其顺序。 它还允许内联层与导入的层交错, 这在块语法中是不可能的。
theme.css
样式规则将覆盖
后续default块中添加的任何规则,
因为层的顺序已被建立:
@layer default, theme, components; @import url ( theme.css ) layer ( theme); @layer default{ audio[ controls] { display : block; } }
也可以通过将@import规则放在@layer规则之间来帮助建立顺序。 这个例子的结果将是相同的:
@layer default; @import url ( theme.css ) layer ( theme); @layer components; @layer default{ audio[ controls] { display : block; } }
然而,@import和@namespace规则必须是连续的, 不能有任何中间规则。 以下示例是无效的:
@import url ( default.css ) layer ( default); @layer theme; @import url ( components.css ) layer ( components); @layer theme{ audio[ controls] { display : block; } }
6.5. 非CSS呈现提示的优先级
用户代理(UA)可以选择在源文档的标记中尊重呈现提示,
例如bgcolor
属性或
s
元素在[HTML]中。
所有基于文档语言的样式必须转换为相应的CSS规则,
并作为规则进入级联,位于
UA来源或特殊的
作者呈现提示来源,位于常规
用户来源和
作者来源之间。
对于级联的目的,
此作者呈现提示来源被视为独立的
来源,
但对于revert关键字的目的,
它被视为作者来源的一部分。
文档语言可以定义这种呈现提示是否作为级联的 UA来源或 作者来源进入; 如果是,则UA必须相应地执行。 例如,[SVG11]将其呈现属性映射到 作者来源中。
注意:作为级联的UA来源规则的呈现提示 可以被作者来源或用户来源的样式覆盖。 作为作者呈现提示来源规则的呈现提示 可以被作者来源的样式覆盖, 但不能被非important的 用户来源样式覆盖。 主机语言在选择呈现提示的适当来源时应考虑这些因素。
7. 默认值
当级联未产生值时, 必须通过其他方式找到指定值。 继承属性通过 继承从其父元素中获得默认值; 所有其他属性都采用其初始值。 作者可以通过使用inherit和initial关键字显式请求继承或初始化。
7.1. 初始值
每个属性都有一个初始值, 在属性的定义表中定义。 如果该属性不是继承属性, 并且级联未产生值, 则该属性的指定值 是其初始值。
7.2. 继承
继承将属性值从父元素传播到其子元素。 元素上某属性的继承值 是其父元素上该属性的计算值。 对于没有父元素的根元素, 继承值是该属性的 初始值。
对于带有影子的[DOM]树,
继承在扁平元素树上运行。这意味着插槽元素继承自其分配的
slot
,
而不是直接从其light tree的父元素继承。
伪元素根据每个伪元素描述的虚拟标签序列继承。[CSS-PSEUDO-4]
某些属性是继承属性, 如其属性定义表中所定义。 这意味着, 除非级联产生一个值, 否则该值将通过继承确定。
属性也可以显式继承。请参阅inherit关键字。
注意:继承遵循文档树,不会被 匿名框截断, 或被框树的操作所影响。
7.3. 显式默认化
下方定义了几个CSS全局属性值; 声明属性具有这些值将显式指定某种默认化行为。 如CSS值与单位所述 [css-values-3], 所有CSS属性都可以接受这些值。
7.3.1. 重置属性:initial关键字
如果属性的级联值 是initial关键字, 则该属性的指定值 是其初始值。
7.3.2. 显式继承:inherit关键字
如果属性的级联值 是inherit关键字, 则该属性的指定值和 计算值 是继承值。
7.3.3. 清除所有声明:unset关键字
如果属性的级联值 是unset关键字, 则如果它是一个继承属性,则其被视为inherit, 如果不是,则视为initial。 该关键字有效地清除声明值在级联中先前发生的所有部分, 正确继承或不继承,具体取决于属性的性质 (或简写属性的所有长手属性)。
7.3.4. 回滚级联来源:revert关键字
如果属性的级联值 是revert关键字, 则行为取决于声明所属的级联来源:
- 用户代理来源
- 等同于unset。
- 用户来源
- 将级联值 回滚到用户代理级别, 使指定值被计算 得好像该元素上未指定任何作者来源或用户来源规则。
- 作者来源
- 将级联值 回滚到用户级别, 使指定值被计算 得好像未指定任何作者来源规则 于此属性的此元素。 对于revert,此来源包括动画来源。
7.3.5. 回滚级联层:revert-layer 关键字
如果属性的级联值 是revert-layer关键字, 则级联值回滚到下面的级联层, 使指定值被计算 得好像在当前级联层中未指定任何规则—— 或在普通和重要级别之间 在级联中——对于此属性的此元素。 但对于在重要元素附加样式中的revert-layer, 它仅回滚元素附加样式和中介的动画来源, 而不回滚任何中介的作者来源中的重要规则。
注意:如果在与revert-layer值相同的 级联来源中没有较低优先级的声明, 则级联值 将回滚到先前的来源。
注意:动画来源不会因revert而与作者来源合并, 因此实际上形成了自己的级联层。
8. 层 API
8.1.
扩展CSSImportRule
接口
CSSImportRule
接口扩展如下:
partial interface CSSImportRule {readonly attribute CSSOMString ?layerName ; };
其layerName
属性表示在at规则中声明的层名称,
如果该层是匿名的,则为空字符串,如果at规则未声明层,则为null。
8.2.
CSSLayerBlockRule
接口
CSSLayerBlockRule
接口表示
@layer块规则:
[Exposed =Window ]interface :
CSSLayerBlockRule CSSGroupingRule {readonly attribute CSSOMString name ; };
其name
属性表示
由at规则本身声明的层名称,
如果该层是匿名的,则为空字符串。
8.3.
CSSLayerStatementRule
接口
CSSLayerStatementRule
接口表示
@layer声明:
[Exposed =Window ]interface :
CSSLayerStatementRule CSSRule {readonly attribute FrozenArray <CSSOMString >nameList ; };
其nameList
属性表示
由at规则声明的层名称列表,
按照与CSSLayerBlockRule
的
name
属性相同的规则进行规范化。
9. 变更
9.1. 自 2021 年 10 月 15 日工作草案以来的变更
自 2021 年 10 月 15 日工作草案以来的非平凡变更:
-
更新了 @import 媒体查询和支持条件的语法样式
-
允许函数符号解析时的别名(问题 6193)
-
使 CSSImportRule.layerName 可为空(问题 6576)
-
澄清了 style 属性中的 revert-layer 不会还原作者层(问题 6743)
-
澄清了 style 属性和关键帧中的 revert-layer 行为(问题 6743)
-
添加了 § 4.1.1 值别名 部分。(问题 6193)
-
澄清了在 style 属性或 @keyframes at 规则中使用 revert-layer 关键字的行为。(问题 6743)
9.2. 自 2021 年 8 月 29 日工作草案以来的变更
自 2021 年 8 月 29 日工作草案以来的变更包括:
-
还原未分层样式的顺序。(见 § 9.3 自 2021 年 6 月 8 日工作草案以来的变更 和 问题 6284)
-
定义展示提示使用作者展示提示来源而不是层,匹配对[CSS-CASCADE-4]的更新。(问题 6659)
9.3. 自 2021 年 6 月 8 日工作草案以来的变更
自 2021 年 6 月 8 日工作草案以来的显著变更包括:
9.4. 自 2021 年 3 月 19 日工作草案以来的变更
自 2021 年 3 月 19 日工作草案以来的显著变更包括:
-
将未分层样式的顺序从正常来源中的最高优先级切换为最低优先级。(问题 6284)
9.5. 自 2021 年 1 月 19 日工作草案以来的变更
自 2021 年 1 月 19 日首次公开工作草案以来的显著变更包括:
-
添加了 revert-layer 关键字。(问题 5793)
9.6. 自第 4 级以来的新增内容
自 第 4 级以来,添加了以下功能:
-
引入了用于定义层的 @layer 规则。
-
为 @import 定义中添加了 layer/layer() 选项。
-
引入了用于回滚值到前一层的 revert-layer 关键字。
9.7. 自第 3 级以来的新增内容
自 第 3 级以来,添加了以下功能:
-
引入了用于回滚层叠的 revert 关键字。
-
为支持条件 @import 规则引入了 supports() 语法。
-
定义了 CSS 用于支持遗留语法的两个属性别名机制。请参阅 § 3.1 属性别名。
9.8. 自第 2 级以来的新增内容
自 第 2 级以来,添加了以下功能:
致谢
David Baron、Tantek Çelik、Florian Rivoal、Simon Sapin、Jen Simmons 和 Boris Zbarsky 为本规范作出了贡献。
隐私和安全考量
-
层叠过程不区分同源和跨源样式表,使得可以通过它们应用于文档的计算样式来推断跨源样式表的内容。
-
通过应用样式规则表达的用户偏好和 UA 默认值,会被层叠过程暴露出来,并且可以通过它们应用于文档的计算样式来推断。
-
@import 规则假定没有
Content-Type
元数据 的资源(或如果主机文档处于怪异模式中的任何同源文件)为text/css
,可能允许任意文件被导入页面并解释为 CSS,从而可能通过它们应用于文档的计算样式推断出敏感数据。