TT Lab
开始
学习 学习路径 课程

CSS — 后写的规则获胜

一旦用上图层,判定就多了一层

在 TT Lab 中继续学习

一句话总结

在层叠的排序顺序中,层叠层比特异性更早分出胜负。所以在已经开始使用层叠层的样式表中,特异性低的规则战胜特异性高的规则,是完全正常的。

为什么需要它

CSS 走向崩坏的路径几乎总是同一条。为了战胜别人的规则,把选择器多写长一截,下一个人写得更长,最后出现了 !important。这样一来,这个属性就只能永远用 !important 来处理。

问题的根源在于,该赢的顺序与特异性本来就是两回事。重置规则应当永远最弱,工具类应当永远最强。但 CSS 里没有办法直接表达这一点,所以直到现在,都是用选择器长度这种间接手段来模拟的。

@layer 是直接用名称写出这种顺序的语法。它由 CSS Cascade 5 定义,MDN 的 @layer 文档 整理了语法。

工作原理——排序顺序

规范第 6.1 节规定的标准,按下面的顺序来看。只要前面分出了胜负,后面就不再看。

1. 출처와 중요도   (사용자 important > 작성자 important > … )
2. 캡슐화 맥락     (섀도 트리)
3. 요소에 직접 붙은 스타일 (style 속성)
4. 레이어
5. 특정성
6. 나온 순서

该代码块中的韩文说明依次为:1 来源与重要性(用户 important > 作者 important > ……);2 封装上下文(影子树);3 直接附在元素上的样式(style 属性);4 层;5 特异性;6 出现顺序。

!important 在第 1 项就分出胜负,所以依然最强。其次是 style 属性,再其次是层叠层,然后才是特异性。

两行就够了

层叠层这一步的规则只有两行。

把这两条合在一起,会得出一个乍看之下相反的结果。

@layer reset, components;

@layer components {
  #app .card .title { color: navy; }   /* 특정성 1,2,0 */
}
.title { color: crimson; }             /* 특정성 0,1,0, 레이어 밖 */

该代码块中的韩文注释依次说明:特异性为 1,2,0;特异性为 0,1,0,位于层外。

获胜的是 .title 的 crimson。层外就是最后一个层,而普通声明是后面的层获胜。特异性根本轮不到去看。

反过来,加上 !important 就会把顺序颠倒过来。reset 层的 !important 会战胜 components 层的 !important,层外的 !important 会输给所有层中的 !important。规范解释说,这与 important 颠倒普通声明优先级是同样的逻辑。

顺序由首次出现的位置决定

层叠层的顺序就是名称首次出现的次序。所以当文件被拆成多个、打包器改变了顺序时,层叠层的顺序也会随之改变。

因此在实际工作中,会在最上面放一行不带块的声明式规则。

@layer reset, base, components, utilities;

这一行就成了整个文件的契约。此后无论哪个文件先被读取,顺序都是固定的。

与 :where() 配对使用

:where() 的特异性始终为 0。:where(.card .title) 是 0,0,0,连一个标签选择器都赢不了。

如果说层叠层决定的是“哪一批获胜”,那么 :where() 就是在同一批之内制造便于覆盖的默认值。库在提供默认样式时把这两者一起使用,使用方就不必再把选择器写得很长。

:is() 和 :not() 则不同——它们遵循括号内最高的那一项。:is(.card, #app) 是 1,0,0。

在现场相遇的样子

下面记录引入层叠层时实际会碰到的三件事。

第一,无法一次性把现有代码全部放进层中。 这时剩下的代码都会成为“层外”,比所有层都强。这不是事故,反而是一条安全的迁移路径——新建的层不会突然覆盖现有页面。

第二,可以给 @import 加上层。 用 @import url(reset.css) layer(reset); 把别人的 CSS 整个塞进一个层,这个库整体就会比我的代码弱。原本为了战胜库而使用的 !important 就全部消失了。

第三,不要在媒体查询中首次声明层。 规范说,只有条件为真时,这个层才会进入顺序。如果样式表的层顺序会随屏幕宽度而变化,就会造成无法复现的缺陷。最上面那一行声明式规则也能防止这种情况。

下一项实验要做什么

声明四个层并填充规则,然后用计算器亲自确认哪个声明获胜。 先把层外为什么最强、加上 !important 为什么会颠倒这两点写进预测文件,然后再用计算器核对。