写过前端的人都踩过这个坑——每次接第三方 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。

什么时候迁移

三层判断:

  1. 项目里有多个 CSS 来源(第三方库 + 自己的样式)
  2. CSS 里出现了 !important
  3. 你发现自己在用 specificity 来打赢样式冲突

满足任意一条,@layer 就值得上。它替代的不是某一条 CSS 规则,而是整个 CSS 架构里最脆弱的那一层——无序的级联。

下一步:找到项目里 CSS 文件顶部的 @import 或 normalize 部分,在它上面加一行 @layer reset, vendor, base, components, utilities;,然后把对应样式搬进去。三个文件就能把整个架构理顺。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 183 阅读