写过三年 CSS,每次被 specificity 坑都要靠堆 class 往上怼——今天 @layer 把这件事从根上彻底变了

写过三年 CSS,你一定遇到过这种情况:组件样式被第三方 CSS 的 ID 选择器卡死,只能用更强的选择器去覆盖;工具类被组件类压住,只好加 !important;BEM 命名写着写着就变成了 .page .card .card__header .card__header__title--highlight,一个属性改了七八处。

这就是 specificity 战争的本质:选择器权重成了代码优先级的裁判,而不是你的意图。


CSS 2022 年出的 @layer 就是来解决这个问题的。它让你在文件顶部声明一整套层叠顺序,后面的层天然比前面的层优先级高,不需要堆选择器,不需要 !important,不需要 BEM 命名约定。

核心用法就一行:

@layer reset, base, components, utilities, overrides;

这行声明了五个层的优先级顺序,越靠后越高。之后你写 CSS 只需要按层分类:

@layer reset {
  * { margin: 0; padding: 0; box-sizing: border-box; }
}

@layer base {
  body { font-family: system-ui; }
  :root { --color-primary: oklch(60% 0.15 250); }
}

@layer components {
  .card { padding: 1rem; background: white; border-radius: 8px; }
  .card h2 { margin-top: 0; color: var(--color-ink); }
}

@layer utilities {
  .mt-4 { margin-top: 1rem; }
  .text-center { text-align: center; }
}

写完之后,不管第三方 CSS 里用了多强的选择器,只要你把自己的代码放在对应层里,优先级就由层叠顺序决定,不用管对方 specificity 有多高。


旧做法 vs 新做法对比

旧做法( specificity 战争):

/* 第三方用 ID,压制一切 */
#calendar .day { color: #333; }

/* 你只能往上怼更强的选择器 */
.page .calendar-wrapper #calendar .day { color: red; } /* 赢了,但丑 */

/* 或者直接 !important */
#calendar .day { color: red !important; } /* 赢了,但埋雷 */

新做法( @layer 隔离):

@layer vendor, base, components, utilities;

/* 把第三方 CSS 隔离进 vendor 层,它永远在 components 和 utilities 之下 */
@import url("/static/vendor/calendar.css") layer(vendor);

@layer components {
  .calendar-day { color: var(--color-ink); } /* 直接赢,不需要任何 hack */
}

只要在 @import 时声明 layer(vendor),第三方样式就被关进了隔离层,你的代码天然在它之上。


Tailwind CSS 4.0 已经在用这个了

Tailwind CSS 4.0 的 Oxide 构建引擎内部用 @layer 管理框架自身的样式层级,官方文档专门提到这件事——它的 utilities 层天然覆盖 components 层,不需要任何 !important。你用的 Tailwind 工具类能稳定覆盖组件样式,背后就是这个机制。

Safari 15.4、Chrome 99、Firefox 97 之后就全部支持了,2022 年的功能,现在全球覆盖率 99%+,不需要任何降级处理。


三个步骤上手

第一步:在 CSS 文件最顶部声明层叠顺序,建议用 reset → base → components → utilities → overrides 这五层结构,根据项目实际情况删减或增加层。

第二步:把现有 CSS 按层分类迁移,不需要改选择器强度,只需要把代码块挪进对应的 @layer {} 块里。

第三步:用 layer(vendor) 包裹所有第三方 CSS 的 @import,把它们隔离进最底层,从此不再被第三方 specificity 卡脖子。


一个原则:永远不用 !important

如果你发现用了 @layer 之后还是需要 !important 才能赢,说明层叠顺序声明错了。!important 会直接跳过层叠顺序,在整个文件里称霸,这不是在解决问题,是在掩盖层顺序设计的问题。找到那个顺序错误,比加 !important 简单得多。

下一步: 打开你的 CSS 文件,在最顶部加一行 @layer reset, base, components, utilities;,然后把现有代码分类扔进去——十分钟就能体验到 specificity 战争消失的感觉。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 16 阅读