摘要

  • W3C 2018 年 2 月草案写下一个尚未命名的零权重选择器;同年 11 月,它以 :where() 之名出现,并用链接下划线的冲突示范用途。
  • :where() 里的条件继续参与匹配,却不给选择器增加权重。括号外的类名、元素名仍照常计分;这让通用样式可以准确,也可以好改。

导航栏里那条链接并没有犯错。通用样式先写了 a:not(:hover),让鼠标没有悬停的链接不带下划线。负责导航栏的人写下 nav a,想把下划线补回来。按直觉,两条规则都选中链接,后写的导航规则该说了算。CSS 却翻出记分牌:前者的伪类加元素,比后者的两个元素选择器更重。把导航规则搬到文件最末尾也没用。于是有人开始加类名、叠伪类,最后祭出 !important,仿佛一根下划线需要动员全公司。

这不是编出来吓新人的事故。它来自 W3C 2018 年 11 月 21 日的 Selectors Level 4 草案。草案给出的改法只有一处:把通用规则写成 a:where(:not(:hover))。未悬停的链接仍会被选中;:not(:hover) 放进 :where() 后,不再给权重添码。括号外的 a 还占一个元素选择器的分量,nav a 则有两个。导航作者不必揣测通用规则为什么这样写,就能在自己的场景里把下划线放回去。

这套办法先有想法,后有名字。2018 年 2 月 2 日草案 的变更记录已提到一个类似 :matches()、权重为零的伪类,但名字仍待确定。11 月草案才把它叫做 :where();CSS 工作组随后发布的说明 也明确列出了这个新名字。问题并非浏览器不会找元素。CSS 早就会找。真正棘手的是:通用规则找到元素后,要不要顺势占住未来的决定权。

选择器平日把两件事绑在一起:匹配谁,以及冲突时谁更有分量。:where() 让作者把这两件事分开。设计系统可以用复杂条件认出组件,再主动降低这些条件在权重赛里的存在感。2022 年 5 月 7 日草案 给出一眼就懂的算式:.qux:where(em, #foo#bar#baz) 的权重是 (0,1,0)。括号里那串吓人的 ID 负责筛选,括号外的 .qux 仍贡献一个类的分数。零权重说的是函数和它的参数,不是把整条规则洗成白纸。

这是共享样式的一种礼貌。基础样式必须有足够的辨认能力,才不会误伤别的元素;但它也可以给局部页面留出改动余地。浏览器负责执行共同的匹配和权重规则,写页面的人决定某条默认样式该有多强势。一个团队提供可复用的外观,另一个团队按具体菜单调节它,双方用同一套算术交接工作,不必比谁能写出更长的选择器。

2022 年 11 月 11 日草案 还记录了函数式选择器列表的解析细节:宽容列表可以舍弃无效项,保留有效项。这使条件组合更有弹性,具体行为仍取决于草案规定的语法。:where() 的核心故事始终是权重的自我约束。W3C 的发布时间线 把这一进程钉在 2018 和 2022 年的公开草案上。网页里那根小小的下划线,提醒了所有写共享 CSS 的人:规则可以认得很准,讲话不必很响。