写过前端的人都踩过这个坑——每次接第三方 UI 库都要和它的样式打架,今天 @layer 把这件事彻底原生化了
前端项目里接一个 Bootstrap 或者 Tailwind,再加一个组件库,再加自己的样式——最后往往要靠 !important 才能让自己的样式赢。这不是你的代码写得不对,是 CSS 本身的级联机制没有给你足够的控制力。
问题出在哪
CSS 解决冲突只看两件事:选择器优先级,和出现顺序。第三方库的选择器写得又长又具体,你的 .btn 打不过它的 .btn.btn-primary[data-state=”active”],唯一的办法就是也写得更长,或者直接 !important。三个项目下来,CSS 里全是补丁。
@layer 做的事情很简单:给你在选择器优先级之上再加一层控制——你可以声明层的顺序,后面的层天然就赢,不需要更高的 specificity。
怎么用
声明顺序只有一次,所有样式共享:
/* 声明顺序,后面赢 */
@layer reset, vendor, base, components, utilities;
然后把样式放进对应层:
@layer reset {
* { margin: 0; padding: 0; box-sizing: border-box; }
}
@layer vendor {
/* Bootstrap / Tailwind 全部扔这里 */
}
@layer components {
.card { background: white; padding: 1.5rem; }
}
@layer utilities {
.mt-4 { margin-top: 1rem; }
}
接第三方库只需要一行:
@import url('bootstrap.css') layer(vendor);
从此以后,你写在 components 或 utilities 里的样式天然赢过 vendor,不需要 !important,不需要更高 specificity。
!important 会被反转
这是很多人不知道的一个细节:正常情况下后面的层赢;但用了 !important 之后,层的顺序会反转——前面的层反而赢。这意味着你可以在低优先级的层里用 !important 做安全网,比如 reset 层写一条 !important 的 box-sizing,确保它不会被任何上层覆盖。
@layer reset, components;
@layer reset {
* { box-sizing: border-box !important; } /* 永远赢 */
}
@layer components {
.box { width: 100%; /* 不会覆盖上面的 box-sizing */ }
}
嵌套层和匿名层
层可以嵌套,用点号访问:
@layer components {
@layer buttons { .btn { ... } }
@layer cards { .card { ... } }
}
/* 引用嵌套层 */
@layer components.buttons {
.btn-new { ... }
}
匿名层没有名字,无法被引用,适合一次性使用的样式:
@layer {
.tmp-fix { display: none; } /* 匿名层,不常用 */
}
未分层样式永远赢
这是最重要的例外:任何不在 @layer 里的样式,优先级高于所有分层样式。所以迁移的时候可以渐进来——现有的样式不用改,它还是最高优先级;新写的样式进层,分层接管新代码。
浏览器支持
2022 年 Chrome 99、Firefox 97、Safari 15.4 就全部支持了。2026 年的今天,全球覆盖率 98%+,不需要任何 polyfill。
什么时候迁移
三层判断:
- 项目里有多个 CSS 来源(第三方库 + 自己的样式)
- CSS 里出现了 !important
- 你发现自己在用 specificity 来打赢样式冲突
满足任意一条,@layer 就值得上。它替代的不是某一条 CSS 规则,而是整个 CSS 架构里最脆弱的那一层——无序的级联。
下一步:找到项目里 CSS 文件顶部的 @import 或 normalize 部分,在它上面加一行 @layer reset, vendor, base, components, utilities;,然后把对应样式搬进去。三个文件就能把整个架构理顺。
评论区
登录后可评论。