Chromium 99、Firefox 97 和 Safari 15.4 Beta 版即将推出级联层(@layer
CSS 规则)。使用样式可以更明确地控制 CSS 文件,以防止样式特异性冲突。这对于大型代码库、设计系统以及在应用中管理第三方样式的情况尤其有用。
明确地对 CSS 进行分层可防止出现意想不到的样式替换,并有助于优化 CSS 架构。
CSS 特异性和级联
CSS 特异性是指 CSS 如何确定要应用于哪些元素的样式。您可以使用的不同选择器决定任何样式规则的特异性。例如,元素没有类或属性具体,而类或属性又不如 ID 具体。这是 CSS 学习的基本组成部分。
人们会使用 BEM 等 CSS 命名惯例来防止无意中覆盖特异性。通过为所有内容指定一个类名,所有内容都放置在相同的特异性平面上。不过,并非总是能保持这种条理清晰的样式,特别是在采用第三方代码和设计系统时。
级联层旨在解决此问题。它们为 CSS 级联引入了一个新的层。使用分层样式时,图层的优先级始终高于选择器的特异性。
例如,选择器 .post a.link
的特异性高于 .card a
。如果您尝试为链接设置样式,那么在帖子中,您会发现应用更具体的选择器。
通过使用 @layer
,您可以更明确地说明每种样式的样式特异性,并确保卡片链接的样式会替换帖子链接的样式,即使从数值上来看,如果所有 CSS 都位于同一平面上,这种特异性可能会较低。这是因为级联优先。分层样式可创建新的级联“平面”。
![分解界面的项目演示图示](https://developer.chrome.google.cn/static/blog/cascade-layers/image/illustration-project-dem-6beee9cc7a3bf.jpeg?authuser=5&hl=sk)
@layer
的实际应用
![演示:使用导入功能显示链接颜色](https://developer.chrome.google.cn/static/blog/cascade-layers/image/demo-showing-link-colors-da82477f07603.png?authuser=5&hl=sk)
此示例展示了使用 @layer
的级联层的强大功能。系统会显示多个链接:一些链接未应用任何额外的类名称,一个链接使用了 .link
类,另一个链接使用了 .pink
类。然后,CSS 会添加三个层:base
、typography
和 utilities
,如下所示:
@layer base {
a {
font-weight: 800;
color: red; /* ignored */
}
.link {
color: blue; /* ignored */
}
}
@layer typography {
a {
color: green; /* styles *all* links */
}
}
@layer utilities {
.pink {
color: hotpink; /* styles *all* .pink's */
}
}
最终,所有链接都显示为绿色或粉色。这是因为:虽然.link
选择器级别特异性高于a
,但a
有一种颜色样式的优先级更高@layer
。当绿色规则位于蓝色规则之后的图层中时,a { color: green }
会替换 .link { color: blue }
。
层优先级高于元素特异性。
组织图层
如上所示,您可以直接在页面上组织图层,也可以在文件的顶部组织图层。
层顺序是各层名称首次出现在您的代码中时确定的。
也就是说,如果您在文件顶部添加以下内容,链接全部会显示为红色,而包含类 .link
的链接会显示为蓝色:
@layer utilities, typography, base;
这是因为层顺序现在颠倒了,将实用程序放在最前面,将实用程序放在最后。因此,base
层中的样式规则的特异性始终高于排版层中的样式规则。这些链接将不再是绿色的链接,而是红色或蓝色的链接。
![Codepen 项目的屏幕截图](https://developer.chrome.google.cn/static/blog/cascade-layers/image/screenshot-codepen-proje-91c531726cd5d.png?authuser=5&hl=sk)
整理导入
使用 @layer
的另一种方法是使用导入文件。您可以在导入样式时使用 layer()
函数直接执行此操作,如以下示例所示:
/* Base */
@import '../styles/base/normalize.css' layer(base); /* normalize or rest file */
@import '../styles/base/base.css' layer(base); /* body and base styles */
@import '../styles/base/theme.css' layer(theme); /* theme variables */
@import '../styles/base/typography.css' layer(theme); /* theme typography */
@import '../styles/base/utilities.css' layer(utilities); /* base utilities */
/* Layouts */
@import '../styles/components/post.css' layer(layouts); /* post layout */
/* Components */
@import '../styles/components/cards.css' layer(components); /* imports card */
@import '../styles/components/footer.css' layer(components); /* footer component */
上述代码段包含三层:base
、layouts
和 components
。base
中的标准化文件、主题文件和排版文件,layouts
中的 post
文件,components
中的 cards
和 footer
文件。导入文件时,图层将使用图层函数进行实例化。另一种方法是在文件顶部组织层,并在执行任何导入前声明这些层:
@layer base,
theme,
layouts,
components,
utilities;
现在,@import
样式的顺序对图层顺序无关紧要,因为图层名称已在第一个实例中建立。这样您就少了一件担心的问题。您仍然可以将导入的文件设置为特定图层,但顺序已经确定。
![来自 Codepen Project 的屏幕截图](https://developer.chrome.google.cn/static/blog/cascade-layers/image/screenshot-codepen-proje-2ee87a6895eab.png?authuser=5&hl=sk)
层和级联
我们回过头来看看层在什么地方使用,因为它与更广泛的级联相关:
优先顺序如下:
- 常规用户代理(优先级最低)
- 本地用户 @layer
- 本地用户常规
- 作者:@layers
- 作者正常
- 作者 !important
- 作者 @layer !important
- 本地用户 !important
- 用户代理 !important**(最高优先级)
您可能会注意到,此处 @layer !important
样式反转了。与非层(普通)样式相比,这种样式没有那么具体,优先级更高。这是因为 !important
在级联中的工作原理:它会打破样式表中的正常级联,并反转正常的层级特异性(优先级)。
嵌套层
图层也可以嵌套在其他图层中。以下示例来自 Miriam Suzanne 的级联层说明:
@layer default {
p { max-width: 70ch; }
}
@layer framework {
@layer default {
p { margin-block: 0.75em; }
}
p { margin-bottom: 1em; }
}
在上面的代码段中,您可以访问 framework.default
,方法是使用 .
作为嵌套在 framework
中的 default
层的记号。您还可以采用更简写的格式编写代码:
@layer framework.default {
p { margin-block: 0.75em }
}
生成的层和层顺序如下:
- 默认
framework.default
framework
未叠加- 未分层的
注意事项
如果使用正确,级联层确实很实用,但也可能会产生额外的困惑和意想不到的结果。使用级联层时,请注意以下几点:
规则 1:不使用 @layer
限定范围
级联层无法解决作用域问题。如果您有一个包含 @layer
的 CSS 文件(例如 card.css
),并且想要设置卡片内所有链接的样式,请不要编写如下样式:
a {
…
}
这样一来,您文件中的所有 a
标记都会获得此替换值。正确限定样式仍很重要:
.card a {
…
}
规则 2:级联层在非层 CSS 之后排序
请务必注意,分层 CSS 文件不会替换非分层 CSS。这是一项刻意的决定,可让您更轻松地以更合理的方式引入层,以便与现有代码库配合使用。例如,使用 reset.css
文件就是很好的起点和级联用例用例。
规则 3:!important
反转级联特异性
虽然分层样式通常不如未分层样式那么具体,但使用 !important
可以反转这一点。在层中,使用 !important
规则的声明比无层样式更具体。
在这种情况下,!important
样式会与其特异性相反。上图显示了这种情况,仅供参考:author @layers 的优先级低于 author @layers 优先级,其优先级低于 author !important 优先级,且优先级低于 author @layer !important。
如果您有多个图层,具有 !important
的第一个图层会优先使用 !important
,并且是最具体的样式。
规则 4:了解注入点
由于图层顺序是每个图层名称首次出现在代码中时确定的,因此如果您在导入并设置 layer()
之后放置 @layer
声明,或者在其他 @layer
语句之后放置该声明,则可以忽略该声明。与在 CSS 中不同,在 CSS 中,页面最靠下的样式规则应用于级联层,首先确定顺序。
可以以列表、图层块或导入的形式保存。如果您使用 layer()
将 @layer
放在导入列表后面,则系统不会执行任何操作。将其放在文件顶部可设置层顺序,并有助于您清楚地看到架构中的层。
第 5 条规则:观察您的特异性
使用级联层时,如果不太具体的选择器位于更具体的层上,则不够具体的选择器(例如 a
)会替换更具体的选择器(例如 .link
)。请注意以下几点:
如果已指定 @layer utilities, components
,则 layer(components)
中的 a
会替换 layer(utilities)
中的 .pink
。虽然这是 API 的特意功能,但如果不预期,您可能会感到困惑和沮丧。
因此,如果您要编写实用程序类,请始终将其作为级别高于要替换它们的组件包含的层。您可能会想,“我刚刚添加了这个 .pink
类来更改颜色,但系统未应用颜色”。
详细了解级联层
您还可以查看以下资源以详细了解级联层: