您的浏览器即将推出级联层

层叠层(@layer CSS 规则)即将在 Chromium 99、Firefox 97 和 Safari 15.4 Beta 中推出。它们可让您更明确地控制 CSS 文件,以防止样式特异性冲突。对于大型代码库、设计系统以及在应用中管理第三方样式时,这特别有用。

以清晰的方式对 CSS 进行分层可以防止意外的样式替换,并有助于构建更好的 CSS 架构。

CSS 特异性和级联

CSS 通过 CSS 特异性来决定将哪些样式应用于哪些元素。您可以使用的不同选择器决定了任何样式规则的具体性。例如,元素的具体程度低于类或属性,而类或属性的具体程度又低于 ID。这是学习 CSS 的基础知识。

为了避免无意中替换特异性,人们会采用 BEM 等 CSS 命名惯例。通过为所有内容分配单个类名称,可将所有内容放置在同一特定性平面上。不过,并非总能保持这种有序的样式,尤其是在使用第三方代码和设计系统时。

带有类的卡片的 BEM 视觉效果
keepinguptodate.com 上关于 BEM 命名的插图示例。

而分层级网络旨在解决此问题。它们会向 CSS 级联引入新的。对于分层样式,图层的优先级始终高于选择器的特异性。

例如,选择器 .post a.link.card a 更具体。如果您尝试为帖子中的卡片内链接设置样式,则会发现系统会应用更具体的选择器。

通过使用 @layer,您可以更明确地指定每种样式的特异性,并确保卡片链接的样式会覆盖帖子链接的样式,即使所有 CSS 都在同一平面上,特异性在数字上可能较低也是如此。这是因为级联优先级。分层样式会创建新的级联“平面”。

项目演示中有关拆分界面的插图

@layer 的实际应用

演示:显示导入内容的链接颜色
查看 Codepen 上的演示

此示例展示了使用 @layer 的级联层的强大功能。系统会显示多个链接:其中一些没有应用任何其他类名称,一个应用了 .link 类,另一个应用了 .pink 类。然后,CSS 会添加三个图层:basetypographyutilities,如下所示:

@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 项目的屏幕截图
查看 Codepen 上的演示

整理导入内容

使用 @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 */

上述代码段包含三个层:baselayoutscomponentsbase 中的标准化、主题和排版文件,layouts 中的 post 文件,以及 components 中的 cardsfooter。导入文件后,系统会使用层函数实例化层。另一种方法是在文件顶部整理图层,在任何导入之前声明它们:

@layer base,
       theme,
       layouts,
       components,
       utilities;

现在,样式的 @import 顺序对图层顺序没有影响,因为图层顺序已在图层名称的第一个实例中确定。这样您就不用担心了。您仍然可以将导入的文件设置为特定图层,但排序已确定。

Codepen 项目的屏幕截图
探索 Codepen 上的项目

图层和级联

我们先来回顾一下,看看层在更广泛的级联中的位置:

级联图示

优先顺序如下:

  • 用户代理正常(最低优先级)
  • 本地用户 @layer
  • 本地用户正常
  • 作者 @layers
  • 作者正常
  • 作者 !important
  • Author @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 进行范围限定

级联层无法解决作用域问题。如果您的 CSS 文件包含 @layer(例如 card.css),并且您想为卡片中的所有链接设置样式,请勿编写如下样式的样式:

a {
  
}

这会导致文件中的所有 a 标记都收到此替换项。不过,正确限定样式仍然很重要:

.card a {
  
}

规则 2:级联层的排序在非分层 CSS 后面

请务必注意,分层 CSS 文件不会替换非分层 CSS。这是我们有意做出的决定,目的是让您能够更轻松地以更合理的方式引入层,以便与现有代码库搭配使用。例如,使用 reset.css 文件是使用分层叠加层的良好起点和用例。

规则 3:!important 会反转级联特异性

虽然分层样式通常不如非分层样式具体,但使用 !important 会使这种情况相反。在图层中,使用 !important 规则的声明比非分层样式更具体。

在这种情况下,!important 样式会反转其特异性。如上图所示:author @layers 的优先级低于 author normal,author normal 的优先级低于 author !important,author !important 的优先级低于 author @layer !important。

如果您有多个图层,则第一个带有 !important 的图层将优先使用 !important,并且是具体性最高的样式。

规则 4:了解注入点

由于图层顺序是在每个图层名称首次出现在代码中时确定的,因此,如果您在导入并设置 layer() 或其他 @layer 语句后放置 @layer 声明,系统会忽略该声明。与 CSS 不同,CSS 会将网页最下方的样式规则应用于级联层,而 HTML 会在首次实例时建立顺序。

它可以位于列表、图层块或导入内容中。如果您在包含 layer() 的导入列表后面放置 @layer,则不会产生任何影响。将其放在文件顶部可设置图层顺序,并帮助您清晰地查看架构中的图层。

规则 5:注意具体性

使用级联层时,如果不太具体的选择器(例如 a)位于更具体的层上,则该不太具体的选择器会替换更具体的选择器(例如 .link)。请考虑以下事项:

如果满足以下条件,layer(components) 中的 a 会替换 layer(utilities) 中的 .pink:指定了 @layer utilities, components。虽然这是 API 的预期行为,但如果您没有预料到,可能会感到困惑和沮丧。

因此,如果您要编写实用程序类,请始终将其作为比您打算用来替换它们的组件更高级别的层级进行添加。您可能会想:“我刚刚添加了这个 .pink 类来更改颜色,但它没有应用。”

详细了解级联图层

您还可以查看以下资源,详细了解级联层: