前端开发如何更好的避免级联层CSS@layer样式冲突?
一、什么是级联层 (Cascade Layers)?
1.1 级联层的官方定义
我们参看 Cascading and Inheritance Level 5(13 January 2022) 中 6.4 节所述:
1.2 级联层为了解决什么问题?

我们原来的 'display' 文案是红色,当我们引入了一个第三方组件库,第三方库中有以下样式。
/* 开发者样式 */
.item {
color: red;
}
/* 第三方库 */
#app .item {
color: green;
border: 5px solid green;
font-size: 1.3em;
padding: 0.5em;
width: 120px;
}
/* 开发者样式 */
#app div.item {
color: red;
}
/* 第三方库 */
#app .item {
color: green;
border: 5px solid green;
font-size: 1.3em;
padding: 0.5em;
width: 120px;
}
或者借助级联中出场顺序对优先级的影响在用户页面中重写
/* 第三方库 */
#app .item {
color: green;
border: 5px solid green;
font-size: 1.3em;
padding: 0.5em;
width: 120px;
}
/* 开发者样式 */
#app .item {
color: red;
}
效果如下:
a { color: blue; }
/* 排序层 */
@layer reset, lib;
/* 通用样式 */
@layer reset {
#app .item {
color: black;
width: 100px;
padding: 1em;
}
}
/* 第三方库样式 */
/*我们将第三方库的样式全部放到lib层*/
/*这里一般使用@import导入的方式*/
/*为了示例简单我们简化了操作*/
@layer lib {
#app .item {
color: green;
border: 5px solid green;
font-size: 1.3em;
width: 130px;
}
}
/* 开发者样式 */
.item {
color: red;
}
为了知道为什么上面的 css 代码能解决冲突问题,更好地理解级联层的作用,理解一些现象背后的根因,了解级联层和级联的关系,我们继续往下看。
二、理解级联层的前提 —— 级联 (cascade)
2.1 什么是级联?
2.2 当前级联的排序标准
- 起源和重要性(Origin and Importance)
- 上下文(Context)
- 样式属性(Element-Attached Styles)
- 层(Layers)
- 特异性(Specificity)
- 出场顺序(又名源代码顺序)(Order of Appearance)
浏览器在确定最终元素样式呈现的时候,会依据这些准则按照优先权从高到低排序,并且会一个一个的检查,直到确定最终样式。
2.3 级联起源(Cascading Origins)
2.3.1 三个核心起源
- 用户代理来源(浏览器内置样式)
- 用户来源(浏览器的用户设置 )
- 作者来源(Web 开发者)
2.3.2 起源的优先级
- 过渡
- 重要的用户代理
- 重要用户
- 重要作者
- 动画
- 普通作者
- 普通用户
- 普通用户代理
越靠前来源的声明优先级越高。过渡和动画我们可以暂时忽略。
2.4 熟悉又陌生的!important
浏览器对具有 'hidden' 类型的 input 输入框设置了默认的展示属性并且将其声明为重要。
input[type=hidden i] { display: none ; }

第二张是样式最终计算结果:隐藏

图片来源:w3.org
下图可以帮助我们直观的理解级联以及级联层在级联中的位置:
图片来源:css-tricks
我们会发现平时操作最多的选择器特异性(selector specificity)只是级联中的一小部分。也轻松地理解了为什么内联样式优先级天然高。同时我们会发现!important 在级联中有特殊地位。他穿插在级联规则的各个阶段并能颠倒优先级。
三、级联层 (CSS@layer) 使用探索
3.1 @layer 是什么?
我们来看 MDN 上的定义:
At-rules 是什么?
3.2 @layer 的句法规则
@layer 的句法如下:
@layer layer-name {rules}
@layer layer-name;
@layer layer-name, layer-name, layer-name;
@layer {rules}
3.3 如何创建级联层
@layer green {
.item {
color: green;
border: 5px solid green;
font-size: 1.3em;
padding: 0.5em;
width: 120px;
}
}
@layer special {
.item {
color: rebeccapurple;
}
}
第二种方法是:创建一个命名的级联层而不分配任何样式。这可以是单层,如下所示:
@layer green;
@layer green, special
一次定义多个层有什么好处呢?
因为声明层的初始顺序决定了层的优先级。与声明一样,如果在多个层中找到声明,最后定义的层声明将最终生效。看下面代码:
@layer green,special;
@layer green {
#app .item {
color: green;
border: 5px solid green;
font-size: 1.3em;
padding: 0.5em;
width: 120px;
}
}
@layer special {
.item {
color: rebeccapurple;
}
}
效果如下图:
@layer green,special;
@layer special {
.item {
color: rebeccapurple;
}
}
@layer green {
.item {
color: green;
border: 5px solid green;
font-size: 1.3em;
padding: 0.5em;
width: 120px;
}
}
效果如下:
@layer {
.item {
color: black;
}
}
这将创建一个匿名级联层,该层功能与命名层相同。但是使用匿名层有如下缺点:
- 可读性较差:匿名层没有名称,会导致样式表不易阅读和理解。特别是在大型项目中,可能会出现样式不断增加,难以跟踪和管理的问题。
- 难以扩展:如果稍后想要更改特定样式或组合,也会很难找到特定的代码块。
- 不可复用性:匿名层中的样式不能在其他地方重复使用或引用。这样会使样式表更难以管理和维护。
@import url(index.css) layer(index);
同时,也支持匿名引入,例如:
@import url(index.css) layer;
3.4 层的嵌套规则
图层可以嵌套。例如:
@layer base {
p { max-width: 70ch; }
}
@layer framework {
@layer base {
p { margin-block: 0.75em; }
}
@layer theme {
p { color: #222; }
}
}
生成的层可以表示为一棵树:
- framework
- base
2.theme
或可以用扁平列表表示
- base
- framework.base
- framework.theme
要将样式附加到嵌套层,您需要使用以下全名来引用它:
@layer framework {
@layer default {
p { margin-block: 0.75em; }
}
@layer theme {
p { color: #222; }
}
}
@layer framework.theme {
/* 这些样式会被添加到framework层里面的theme层 */
blockquote { color: rebeccapurple; }
}
看效果:
3.5 层的排序规则
级联层按照它们声明的顺序排序。第一层优先级最低,最后一层优先级最高。但是,未分层的样式具有最高优先级:
/* 未分层 */a { color: green; }
@layer layer-1 { a { color: red; } }
@layer layer-2 { a { color: orange; } }
@layer layer-3 { a { color: yellow; } }
优先级顺序如下:
- 未分层样式
- layer-3
- layer-2
- layer-1
看下图示例:
层可以在一个地方被定义图层(以建立图层顺序),然后在任何地方添加样式
/* 定义在一个地方 */
@layer my-layer;
/* 其他样式*/
...
/* 在某个地方添加样式 */
@layer my-layer { a { color: red; } }
3.6 加上!important 之后的排序规则
/* 未分层 */ a { color: green ; }
@layer layer-1 { a { color: red ; } }
@layer layer-2 { a { color: orange ; } }
@layer layer-3 { a { color: yellow ; } }
任何加上重要声明的样式都以相反的顺序应用
优先级顺序如下:
- !important layer-1
- !important layer-2
- !important layer-3
- !important 未分层样式
看下图示例:
这里我们看到对应规则在 chrome 浏览器的显示是正确的。但是在开发者控制台中的样式一栏规则显示有问题。应该是 chrome 浏览器开发者控制台的 bug。
3.7 嵌套层的排序规则
@layer layer-1 { a { color: red; } }
@layer layer-2 { a { color: orange; } }
@layer layer-3 {
@layer sub-layer-1 { a { color: yellow; } }
@layer sub-layer-2 { a { color: green; } }
/* 未嵌套 */ a { color: blue; }
}
/* 未分层 样式 */ a { color: black; }
优先级顺序如下:
- 未分层 样式
- layer-3-layer-3 未嵌套-layer-3 sub-layer-2-layer-3 sub-layer-1
- layer-2
- layer-1
3.8 媒体查询对层排序的影响
以下层顺序将取决于匹配的媒体条件:
例如:
@media (min-width: 600px) {
@layer layout {
.item {
font-size: x-large;
}
}
}
@media (prefers-color-scheme: dark) {
@layer theme {
.item {
color: red;
}
}
}
如果两个媒体查询的规则中匹配一个那么对应的级联层生效。如果两者都匹配,那么图层顺序将为 layout, theme 都生效。如果两个都不匹配则不定义层。下图是两者都匹配的场景。
四、现在就能使用级联层吗?
4.1 目前浏览器支持程度
目前所有现代浏览器均已经支持 @layer 规则。所有浏览器厂商都支持的特性未来一定比较实用。
4.2 W3C 鼓励可以作为日常使用
SS 的标准化流程由 W3C Cascading Style Sheets Working Group (CSSWG)——W3C 层叠样式列表小组以及独立 CSS 专家组成。W3C 本身并不制定标准,而是作为一个论坛式的平台,接收来自小组成员的提交,并通过会议来商讨制定标准,所有的提交以及讨论都是公开透明的,可以在 W3C 网站上看到会议的记录,可以简单分为 4 个大阶段:
- 工作草案 (WD)
- 候选人推荐 (CR)
- 提议的建议 (PR)
- W3C 推荐 (REC)
下图可以帮助理解:
图片来源:w3.org
W3C 通过状态码表示规范的成熟度。成熟度从低到高排序如下图。
图片来源:w3.org
再看下图:包含 layer 概念的标准讨论已经到达 CR 阶段。
图片来源:w3.org
五、总结
/* 排序层 */
@layer reset, lib;
/* 通用样式 */
@layer reset {
#app .item {
color: black;
width: 100px;
padding: 1em;
}
}
/* 第三方库样式 */
/*我们将第三方库的样式全部放到lib层,这里一般使用@import导入的方式,为了示例简单我们简化了操作*/
@layer lib {
#app .item {
color: green;
border: 5px solid green;
font-size: 1.3em;
width: 130px;
}
}
/* 开发者样式 */
.item {
color: red;
}

级联层(CSS@layer)已经历概念提出到到浏览器全面支持的阶段。也许在不久的将来大家都会普遍使用它,期望本文能给大家带来一定帮助。