问题场景
接手一个老项目,CSS 维护的人多了,出了一个经典死局:
css
/* reset.css */
.button { color: blue; }
/* 业务组件 */
.button { color: red; }
/* 第三方组件库(被强制覆盖) */
.button { color: green !important; }
想改第三方库的颜色,只能跟着上 !important;改完发现别的 !important 又把它们压下去,于是代码里出现一堆 !important 互踩、z-index: 99999 满天飞。因为优先级完全取决于 "谁在文件后面的加载顺序" 和 "选择器特异性",而这两个因素在大型项目里根本不可控。
你需要的不是再叠一个 !important,而是明确声明样式分层的优先级契约 ------这就是 @layer 存在的意义。
原因分析
CSS 的层叠(cascade)本来就该由作者意图决定,但传统 CSS 只给了两个粗糙的旋钮:
- 选择器特异性(ID > 类 > 标签)
- 源码顺序(后面的覆盖前面的)
可一旦有第三方库、你无法控制引入顺序、或者不得不写 !important,这两个旋钮就失效了。!important 的本质是"绕过一切规则",它让层叠彻底失控。
@layer 则提供第三维:显式的层优先级 。你可以声明 layer A 永远压在 layer B 上面,与选择器特异性、书写顺序完全解耦。
解决方案
1. 基础用法:@layer 声明层
css
/* 声明层的优先级顺序:越靠后声明的层,优先级越高(针对同层级的规则) */
@layer reset, base, components, utilities;
/* 把规则放进对应的层 */
@layer reset {
.button { margin: 0; color: blue; }
}
@layer components {
.button { color: red; }
}
@layer utilities {
.button { color: green; }
}
最终 .button 显示 green (utilities 在声明顺序的最后)。哪怕 reset 里的 .button 是 !important 之外的相同权重,也由层决定胜负。
2. 层内仍然遵循特异性
@layer 只是"层间"排序,层内依然按特异性+顺序正常工作:
css
@layer components {
.button { color: red; } /* 特异性 0-1-0 */
#special-button { color: blue; } /* 特异性 1-0-0 → 赢 */
}
3. !important 在层里是"反着来"的 ⚠️
这是最反直觉的坑:!important 的优先级在 @layer 里会反转 ------越靠前 声明的层的 !important 权重越高。
css
@layer reset, components;
@layer components {
.button { color: red !important; }
}
@layer reset {
.button { color: blue !important; }
}
/* blue 赢,因为 reset 层更靠前 */
所以别在 @layer 里用 !important,否则你会被反转规则坑哭。
4. 用 @import 把第三方库塞进层里
这是治理第三方库样式的最强用法------无需改库源码:
css
/* 传统写法:第三方样式无法分层 */
@import url("bootstrap.css");
/* @layer 写法:把第三方库整体压到最低优先级层 */
@import url("bootstrap.css") layer(vendor);
之后你自己写的任何常规样式,天然就能覆盖 Bootstrap,不用再跟它的特异性死磕。
5. 未分层样式 > 任何层
注意:不放在任何 @layer 里的普通样式,优先级高于所有嵌入层的样式 (除非碰上反转的 !important)。常用套路:normalize/reset 放 @layer reset,业务代码放层外,保证业务代码永远赢。
css
@layer reset, base, components;
@layer reset { * { margin: 0; } }
@layer components { .card { padding: 16px; } }
/* 层外 ------ 最高优先级,放心覆盖 */
.card--hot { border: 2px solid red; }
要点总结
@layer reset, base, components;声明顺序 = 优先级从低到高,后面声明的层获胜。- 层间排序独立于选择器特异性和书写顺序 ,这是它比
!important高明的地方。 - 层内仍按特异性 + 源码顺序 判断,别指望
@layer抹平层内的差异。 !important在@layer里优先级反转(靠前的层更重),务必避免在层内使用。- 用
@import url(...) layer(name)把第三方库压进低优先级层,从此覆盖库样式不再痛苦。 - 未分层的普通样式 > 所有层,适合放必须压过一切的业务/兜底样式。
- 浏览器支持:2022 年起全线主流浏览器(Chrome/Edge/Firefox/Safari)均稳定支持,可放心用于新项目。
一句话:
@layer把"谁说了算"从混乱的加载顺序和特异性,变成了一份写在文件顶部的清晰优先级契约 。治理大型项目样式,它比!important靠谱一百倍。