W3C

CSS 作用域模块 第 1 级

W3C 首次公开工作草案, 2014年4月3日

本版本:
http://www.w3.org/TR/2014/WD-css-scoping-1-20140403/
最新版本:
http://www.w3.org/TR/css-scoping-1/
编辑草案:
http://dev.w3.org/csswg/css-scoping/
反馈:
www-style@w3.org 主题行为 “[css-scoping] … message topic …”(存档
测试套件:
尚无
编辑:
Tab Atkins Jr.Google
Elika J Etemad特邀专家

摘要

本规范定义了 CSS 的各种作用域/封装机制,包括作用域样式和 @scope 规则、Shadow DOM 选择器,以及基于页面/区域的样式。 CSS 是一种用于描述结构化文档(如 HTML 和 XML)在屏幕、纸张、语音等上的呈现的语言。

本文档状态

本节描述了本文档在发布时的状态。其他文档可能会取代本文件。当前 W3C 发布物的列表及本技术报告的最新修订版可在 W3C 技术报告索引 http://www.w3.org/TR/ 查阅。

本文件是 首次公开工作草案

作为首次公开工作草案发布,并不意味着 W3C 成员的认可。此为草案文件,可能随时被更新、替换或废弃。除作为进行中的工作外,不应引用本文件。

存档)公共邮件列表 www-style@w3.org(见 说明)是讨论本规范的首选渠道。发送邮件时,请在主题中注明“css-scoping”,建议格式如下: “[css-scoping] …评论摘要…

本文件由 CSS 工作组(隶属于 样式活动)制定。

本文件由遵循 2004年2月5日 W3C 专利政策 的工作组制定。W3C 保有 与本组交付物相关的公开专利披露列表;该页面还包括专利披露说明。知晓某项专利且认为其中包含 必要权利要求 的个人,须依 W3C 专利政策第6节 披露相关信息。

目录

1 引言

...

2 作用域样式

作用域样式规则 仅适用于文档的某个子树, 而不是匹配整个文档。 作用域有两个主要影响:

2.1 作用域机制

样式规则可以通过文档语言中定义的结构 或者通过 CSS 中的@scope规则进行作用域限制。

2.1.1 用于作用域的文档标记

文档语言可以定义机制,让样式表作用于文档中的某个元素。 例如,在 HTML 中, 带有style元素和scoped属性 的样式表会作用于该style元素的父元素。 [HTML]

2.1.2 CSS 作用域语法:@scope规则

@scope 规则允许作者通过 CSS 语法编写作用域样式规则。 @scope规则的语法如下:

@scope <selector> {
  <stylesheet>
}

其中,被<selector>匹配到的元素 是作用域根, 用于内部<stylesheet>的样式规则。 被@scope作用域限定的样式规则选择器 被约束在其作用域根内。

该规则使作者很容易创建作用域样式表, 这可能会影响作用域样式实现的优化策略。

如果有多个元素匹配<selector><stylesheet>会被有效地复制, 并且分别独立地应用于每个匹配元素。 作者应避免使用过于通用的选择器, 否则可能会与级联产生令人困惑的交互效果。

作用域样式表不仅附加在最外层的作用域元素上, 还会附加在所有匹配的元素上。 例如,给定如下样式表
@scope div {
  span {
    color: blue;
  }
}
@scope section {
  span {
    color: orange;
  }
}

以及如下文档片段

<div>
  <section>
    <div>
      <span>text</span>
    </div>
  </section>
</div>

其中的文本将会是蓝色。

@scope规则可以嵌套。 在这种情况下,与嵌套样式规则类似, 外层@scope的选择器 作用域包含内层的选择器。

@scope规则内部选择器的优先级是局部计算的: 用于指定作用域元素的选择器会被忽略。 但由于作用域内样式会覆盖非作用域样式, @scope内部的规则会覆盖其外部的规则。

在以下示例中,文本将会是绿色:
  @scope aside {
    p { color: green; }
  }
  aside#sidebar p { color: red; }

如果多个@scope规则应用于同一个元素, 它们是否应该按优先级进行级联?

2.2 查询作用域上下文

2.2.1 选择作用域根::scope伪类

在作用域样式表中, :scope伪类, 定义见[SELECTORS4], 匹配作用域根

2.2.2 选择作用域外部::scope-context()伪类

该机制的定义方式与:host-context()类似, 但匹配的是作用域根的祖先。

然而,对于作用域样式表,你可能希望能够对外部树使用复杂选择器进行匹配, 而非仅仅是单一的复合选择器, 所以我们或许需要采用一种不会在语法上反转树元素顺序的更通用机制。

可能的设想:

:scope-context(<selector>) div {...}
scope(<selector>) div {...}
\scope <selector>\ div {...}
<selector> \scope\ div {...}

该功能将取代@global,因为@global作为选择器并不理想。

3 Shadow 封装

Shadow DOM 规范为 DOM 增加了若干新概念, 其中有些与 CSS 相关。

影子树是一段文档片段, 可以附加到 DOM 的任意元素上。 影子树的根是影子根, 它是一个与影子宿主相关联的非元素节点。 一个元素可以有任意数量的影子树, 并按创建时间排序。 某元素最新创建的影子树 就是该元素的活动影子树

带有影子树的元素称为影子宿主。 它对于其影子树来说是宿主元素

影子宿主的后代 不应在格式化树中生成盒子。 相反,活动影子树的内容 会像元素内容一样生成盒子。

在 shadow DOM 的某些情况下, 元素没有元素父节点 (而是有影子根或其他东西作为父节点)。 没有父节点或父节点不是元素的元素, 被称为顶层元素

尽管影子宿主的子元素不会正常生成盒子, 它们可以被显式分配到影子树并强制正常渲染。 这是通过将元素分配到分发列表来完成的。 带有分发列表的元素被称为插入点

本规范不定义如何将元素分配到分发列表, 而是交由 Shadow DOM 规范处理。 在本规范编写时, 只有content元素在影子树中可以拥有分发列表

插入点不能生成任何盒子。 相反,其分发列表中的元素会正常生成盒子, 就像它们直接替换插入点一样。 (类似于display-box: contents的行为。)

3.1 Shadow DOM 选择模型

元素树中的元素 还可以拥有零个或多个影子树 以及零个或一个分发列表

注意:元素的“后代” 是基于元素的子元素, 不包括元素的影子树分发列表

当选择器匹配影子树时, 初始选择器匹配列表 包括影子宿主, 后面是影子树的所有顶层元素 及其后代, 按照先序遍历排序。

3.1.1 影子树中的宿主元素

影子宿主处于其所承载的影子树之外, 但有时需要在影子树上下文中为其设置样式。

对于选择器而言, 宿主元素也会出现在其每一个影子树中, 此时影子树的内容被视为它的子元素。 如果一个元素有多个影子树, 它会分别在每个影子树的上下文中出现; 每个影子树只看到自己作为宿主元素的内容, 而看不到其他影子树

宿主元素不能通过任何方式 被选择, 除了:host:host-context() 伪类。 也就是说,在该上下文中影子宿主 没有标签名、ID、类名或属性, 唯一额外的信息就是:host伪类能够匹配它。 特别地,宿主元素也不会被*选择器匹配。

为什么影子宿主这么特殊?

影子宿主处于影子树之外, 其标记由页面作者控制, 而不是组件作者。

如果一个组件在影子树内使用了某个类名, 页面作者在使用该组件时无意中也用同样的类名给宿主元素添加了该类名, 就会产生组件作者无法预期、页面作者难以调试的意外样式。

但仍有一些合理场景需要让影子树中的样式表 为宿主元素设定样式。 因此,为了允许这种情况并避免意外样式, 宿主元素出现但完全没有特征, 只能通过:host选择。

3.2 Shadow DOM 选择器

Shadow DOM 定义了一些新选择器, 用于和 Shadow DOM 相关的元素选择。

本节仍在讨论中, 欢迎就以下功能的直观语法提出建议和反馈。

3.2.1 亮区选择::host:host():host-context()伪类

:host 伪类, 在影子树上下文中计算时, 匹配影子树宿主元素。 在其他上下文中, 匹配不到任何元素。

:host() 函数伪类 语法如下:

:host( <compound-selector> )

影子树上下文中计算时, 如果宿主元素 在其普通上下文中匹配该选择器参数,则匹配该元素。 在其他上下文中, 匹配不到任何元素。

例如,假设你有一个组件,其影子树如下:
  <x-foo class="foo">
    <"shadow tree">
      <div class="foo">...</div>
    </>
  </x-foo>

对于影子树内部的样式表:

通常情况下,影子树内的选择器 不能看到影子树之外的任何元素。 但有时,选择影子树上方文档中的某个祖先是有用的。

例如,一组组件可以定义一些它们能够响应的配色主题。 页面作者可以通过在组件或更高层的文档结构中添加特定类来选择某个主题。

:host-context() 函数伪类可测试在影子树外部是否有某个祖先 匹配给定选择器。 其语法为:

:host-context( <compound-selector> )

影子树上下文中计算时, :host-context() 伪类会匹配宿主元素, 如果宿主元素或其某个祖先匹配参数选择器。 对于该伪类而言, 元素的“祖先”定义如下:

如果元素被分发到分发列表
即其最终分发到的content元素。
如果元素是影子树的顶层元素
宿主元素
否则
即该元素的父节点(如果存在)。

注意:这意味着该选择器会穿透 shadow 边界向上查找, 直到遇到文档根节点为止,查找所有匹配参数的元素。

3.2.2 选择暗区:::shadow 伪元素

如果一个元素拥有至少一个影子树::shadow 伪元素会匹配影子根本身。 在 HTML 中,影子根ShadowRoot 对象表示。

::shadow 伪元素不能生成盒子, 除非其他规范另有规定。 但对于选择器而言, ::shadow 伪元素被视为影子树的根节点, 影子树中的顶层元素::shadow 伪元素的直接子元素。

例如,假设你有一个组件,其影子树如下:
  <x-foo>
    <"shadow tree">
      <div>
        <span id="not-top">...</span>
      </div>
      <span id="top">...</span>
    </>
  </x-foo>

对于外部文档中的样式表, x-foo::shadow > span 匹配 #top, 而不会匹配 #not-top, 因为它不是影子树中的顶层元素

如果想要选中 #not-top, 可以使用 x-foo::shadow > div > span。 但这种做法会让样式强依赖于组件的内部结构; 更多时候,建议使用后代组合器, 如 x-foo::shadow span, 以选择影子树中所有相应类型的元素。

如果某个元素有多个影子树, 一个::shadow伪元素会选中 所有对应的影子根

同样, 在影子树内部, 使用类似 :host::shadow div 的选择器 会选中该元素所有影子树内的 div 元素, 而不是只选中当前选择器所在的影子树。

3.2.3 选择 Shadow 投影内容:::content 伪元素

::content 伪元素匹配拥有分发列表的元素本身。

::content 作为名称过于宽泛, 其实它只针对影子树的投影内容。

::content 伪元素不能生成盒子, 除非其他规范另有规定。 但对于选择器而言, ::content 伪元素被视为 分发列表中元素的父节点。

例如,假设你有一个同时拥有子元素和影子树的组件,如下:
  <x-foo>
    <div id="one" class="foo">...</div>
    <div id="two">...</div>
    <div id="three" class="foo">
      <div id="four">...</div>
    </div>
    <"shadow tree">
      <div id="five">...</div>
      <div id="six">...</div>
      <content select=".foo"></content>
    </"shadow tree">
  </x-foo>

对于影子树内的样式表, 类似 ::content div 的选择器 会选中 #one#three#four, 因为这些元素由唯一的 content 元素投影而来, 而 #two 不会被选中。

如果只希望选中 content 元素投影的顶层元素, 可以使用子代组合器, 如 ::content > div, 这样 #four 就不会被选中, 因为它不是 ::content 伪元素的子元素。

注意:::content div 选择器 等价于 *::content div, 其中 * 会选中更多元素而不仅仅是 content 元素。 但实际上只有 content 元素拥有分发列表, 也只有它拥有 ::content 伪元素。

3.2.4 穿透 Shadow 选择:/deep/ 组合器

当选择器中遇到 /deep/ 组合器时, 会将 选择器匹配列表中的每个元素 替换为从原始元素出发、通过任意数量的子元素列表或影子树遍历可达的所有元素。

例如,假设你有一个组件,其影子树如下:
  <x-foo>
    <"shadow tree">
      <div>
        <span id="not-top">...</span>
      </div>
      <span id="top">...</span>
      <x-bar>
        <"shadow tree">
          <span id="nested">...</span>
        </>
      </x-bar>
    </>
  </x-foo>

对于外部文档中的样式表, 选择器 x-foo /deep/ span 会选中所有三个 <span> 元素: #top#not-top,以及 #nested

这本质上是超级后代组合器。 如果后代组合器有专门的符号, 或许可以直接将其重复两次。 或许我们可以给后代组合器一个别名 >>, 因为它其实是超级子代组合器? 那么 /deep/ 就可以写作 >>>

3.3 Shadow 级联与继承

3.3.1 级联

为了实现针对影子根元素的级联行为, 本规范扩展了级联规范中定义的 级联顺序[CSS3CASCADE]

需要在 Origin 和 Scope 之间增加一个级联判据, 叫作 Shadow Tree(影子树)。

计算出现顺序时, 使用 Shadow DOM 规范定义的树结构来计算顺序。

3.3.2 继承

影子树顶层元素 从其宿主元素继承属性。

分发列表中的元素 从其最终分发到的content元素的父元素继承属性, 而不是从其正常父元素继承。

4 分片样式

分片内容可以根据其出现的行、列、页面、区域等位置 被赋予不同的样式。 这可以通过使用合适的 分片伪元素 实现, 允许只针对元素的特定片段而不是整个元素进行样式定义。

在本示例中, 设计师希望 流入 #region1 的文本 变为深蓝色并加粗。 该设计可以如下表达:
#region1::region p {
  color: #0C3D5F;
  font-weight: bold;
}

这里 ::region 伪元素后面跟着 p 相对选择器。 color 和 font-weight 声明会应用于 显示在 #region1 的所有段落片段。 下图展示了 如果我们为 #region1 应用该样式后 渲染效果的变化。 可以注意到,在字体加粗后, 同样的盒子中能容纳的文本变少了。

Illustrate how changing region styling affects the flow of content.
不同区域样式下的不同渲染效果

注意:该特性是 ::first-line 样式的扩展。

4.1 基于区域的样式:::region 伪元素

扩展以指定:

::region 伪元素表示一个选择器(匹配CSS 区域)与一个相对选择器(匹配某些命名流内容)之间的关系。 这允许样式声明应用于流入特定区域的命名流内容片段。

<region selector>::region <content selector>  {
    ... CSS 样式声明 ...
}

当 ::region 伪元素附加到一个 匹配一个或多个CSS 区域选择器时, 会创建一个“流片段”选择器。 流片段选择器指定了 流中哪些元素范围 可以被相对选择器匹配。 相对选择器可以匹配命名流中显示在所选区域(可完全或部分显示,见[DOM])的元素范围。

完全或部分处于流片段范围的元素都可能被相对选择器匹配。 但样式声明只应用在显示于相应区域的元素片段上。

只有有限的属性可以应用于 ::region 伪元素:

这个列表要么应该包含所有具有功能性继承的属性,要么包含所有属性。 为什么它只是所有属性的一个看似随意的子集,包括盒子属性?

  1. 字体属性
  2. 颜色属性
  3. 透明度属性
  4. 背景属性
  5. word-spacing
  6. letter-spacing
  7. text-decoration
  8. text-transform
  9. line-height
  10. 对齐和分散属性
  11. 边框属性
  12. 圆角属性
  13. 边框图像属性
  14. 外边距属性
  15. 内边距属性
  16. text-shadow
  17. box-shadow
  18. box-decoration-break
  19. width

在以下示例中,命名流 “article-flow” 流入 “region-1” 和 “region-2”。

<style>
  #div-1 {
    flow-into: article-flow;
  }

  #region-1, #region-2 {
    flow-from: article-flow;
  }

  /* 区域样式 */
  #region-1::region p  {
    margin-right: 5em;
  }
</style>

<body>
  <div id="div-1">
      <p id="p-1">...</p>
      <p id="p-2">...</p>
  </div>
  <div id="region-1"></div>
  <div id="region-2"></div>
</body>
展示命名流内容如何填充区域以说明区域样式的示例。

该区域样式应用于适配 region-1 的流内容。 相对选择器会匹配 p-1p-2, 因为这些段落 流入 region-1。 只有 p-2 流入 region-1 的片段 会被伪元素样式化。

::region 伪元素中的所有选择器 都会贡献其特异性。 因此上述例子中 ::region 伪元素的特异性 会把 id 选择器的特异性与类型选择器的特异性相加, 得到特异性 101。

匹配指定元素或元素片段(如上所述)的选择器, 参与 CSS 级联顺序, 见[CSS21]

区域样式不适用于嵌套区域。例如,如果区域 A 从包含区域 B 的流中接收内容,则流入 B 的内容不会获得为区域 A 指定的区域样式。

我们需要某种方式来查询特定区域中片段的样式。 getComputedStyle() 不够, 因为一个元素可以存在于多个区域,每个片段可能有不同样式。

符合性

文档约定

符合性要求采用描述性断言与 RFC 2119 术语结合表达。本规范中规范性部分出现的 "MUST"、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY” 和 “OPTIONAL” 等关键词,按照 RFC 2119 的解释理解。 但为便于阅读,本规范不会将这些词全部大写。

除明确标记为非规范性、示例和注释的部分外,本规范的所有内容均为规范性内容。[RFC2119]

本规范中的示例以“例如”开头,或通过 class="example" 与规范性文本区别显示,如下:

这是一个信息性示例。

信息性注释以“注意”开头,并通过 class="note" 与规范性文本区别显示,如下:

注意,这是一条信息性注释。

符合性类别

本规范的符合性 定义了三类符合性对象:

样式表
一份 CSS 样式表
渲染器
一个 UA 解析样式表语义并渲染 使用这些样式表的文档。
创作工具
一个 UA 用于编写样式表。

如果样式表中使用了本模块定义的语法,其所有语句均应根据通用 CSS 语法与本模块定义的各特性语法被判定为有效,则该样式表符合本规范。

渲染器符合本规范指在按相关规范解析样式表的同时,能够正确解析并渲染本规范定义的所有特性。但因设备限制导致 UA 无法正确渲染文档,并不视为不符合规范。(例如,UA 并不要求在单色显示器上渲染颜色。)

创作工具符合本规范指其输出的样式表在语法上符合通用 CSS 语法和本模块中各特性语法,并满足本模块描述的样式表所有其他符合性要求。

部分实现

为便于作者利用向前兼容的解析规则赋予回退值,CSS 渲染器必须将不支持的 at 规则、属性、属性值、关键字及其它语法结构等视为无效并适当忽略。尤其是,用户代理不得在单个多值属性声明中选择性忽略不支持的组件值而保留支持的值:若某一值无效(不支持的值必须如此),CSS 要求整个声明被忽略。

实验性实现

为避免与未来 CSS 特性冲突,CSS2.1 规范为 CSS 的专有与实验性扩展保留了带前缀的语法

在规范进入 W3C 候选推荐阶段前,所有 CSS 特性的实现都视为实验性实现。CSS 工作组建议实现时采用厂商前缀语法,包括 W3C 工作草案中的特性。这样可避免与未来草案变更产生不兼容。

非实验性实现

一旦规范进入候选推荐阶段,即可进行非实验性实现,且实现者应发布任何 CR 级别特性的无前缀实现,只要能证明其正确实现。

为确保 CSS 在各实现间的互操作性,CSS 工作组要求非实验性 CSS 渲染器在发布任何 CSS 特性的无前缀实现前,向 W3C 提交实现报告(如有必要,还应提交相应的测试用例)。提交给 W3C 的测试用例将由 CSS 工作组审核和修正。

有关提交测试用例和实现报告的更多信息,请参见 CSS 工作组网站 http://www.w3.org/Style/CSS/Test/。 如有疑问,请发送邮件至 public-css-testsuite@w3.org 邮件列表。

参考文献

规范性引用

[CSS21]
Bert Bos 等. 层叠样式表 2级 修订版1 (CSS 2.1) 规范. 2011年6月7日. W3C 推荐. URL: http://www.w3.org/TR/2011/REC-CSS2-20110607
[CSS3CASCADE]
Håkon Wium Lie; Elika J. Etemad; Tab Atkins Jr.. CSS 级联与继承 3级. 2013年10月3日. W3C 候选推荐. (进行中). URL: http://www.w3.org/TR/2013/CR-css-cascade-3-20131003/
[DOM]
Anne van Kesteren; Aryeh Gregor; Ms2ger. DOM Living Standard. WHATWG Living Standard. (进行中). URL: http://dom.spec.whatwg.org/
[RFC2119]
S. Bradner. RFC 中用以指示需求级别的关键词. URL: http://www.ietf.org/rfc/rfc2119.txt

资料性引用

[HTML]
Ian Hickson. HTML. 活标准. URL: http://www.whatwg.org/specs/web-apps/current-work/multipage/
[SELECTORS4]
Elika J. Etemad; Tab Atkins Jr.. 选择器 4级. 2013年5月2日. W3C 工作草案. (进行中). URL: http://www.w3.org/TR/2013/WD-selectors4-20130502/

索引

属性索引

未定义属性。

问题索引

该规则使作者很容易创建作用域样式表, 这可能会影响作用域样式实现的优化策略。
如果多个@scope规则应用于同一个元素, 它们是否应该按优先级进行级联?
该机制的定义方式与:host-context()类似, 但匹配的是作用域根的祖先。

然而,对于作用域样式表,你可能希望能够对外部树使用复杂选择器进行匹配, 而非仅仅是单一的复合选择器, 所以我们或许需要采用一种不会在语法上反转树元素顺序的更通用机制。

可能的设想:

:scope-context(<selector>) div {...}
scope(<selector>) div {...}
\scope <selector>\ div {...}
<selector> \scope\ div {...}

该功能将取代@global,因为@global作为选择器并不理想。

本节仍在讨论中, 欢迎就以下功能的直观语法提出建议和反馈。
::content 作为名称过于宽泛, 其实它只针对影子树的投影内容。
这本质上是超级后代组合器。 如果后代组合器有专门的符号, 或许可以直接将其重复两次。 或许我们可以给后代组合器一个别名 >>, 因为它其实是超级子代组合器? 那么 /deep/ 就可以写作 >>>
扩展以指定:
这个列表要么应该包含所有具有功能性继承的属性,要么包含所有属性。 为什么它只是所有属性的一个看似随意的子集,包括盒子属性?
我们需要某种方式来查询特定区域中片段的样式。 getComputedStyle() 不够, 因为一个元素可以存在于多个区域,每个片段可能有不同样式。