CSS 优先级与特异性
解释特异性如何参与层叠比较,纠正“行数靠后一定赢/权重 100/10/1 算术”的简化误区,并给出工程中可维护的优先级策略。
#type / concept
#status / growing
#tech / dev / frontend
#resource / css
[!info] 关联笔记
- 所属:层叠与选择器 MOC · CSS MOC
- 路线:CSS 学习路线
- 并列:层叠、继承与源顺序 · 选择器
- 工程:BEM · CSS Modules · Tailwind
CSS 优先级与特异性
这个概念为什么出现
同一元素被多条规则命中时,浏览器必须决定用哪一条声明。若团队不懂特异性,就会陷入:
- 不断加更长选择器“压过去”
- 滥用
!important - 改一处样式,三处布局炸裂
特异性(specificity)是层叠比较中的关键维度之一。
[!abstract] 一句话理解 特异性衡量选择器有多“具体”;在来源与层叠层相同的前提下,更具体的选择器胜出,再相同才看源顺序。
最小可运行示例
场景:库存状态徽章被全局样式误伤
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>specificity lab</title>
<style>
/* A: 元素选择器 */
span { color: black; }
/* B: 类选择器 */
.badge { color: #334155; }
/* C: 双类 */
.badge.badge--danger { color: #b91c1c; }
/* D: ID(演示用,业务中应谨慎) */
#sku-badge { color: #0369a1; }
</style>
</head>
<body>
<span id="sku-badge" class="badge badge--danger">低库存</span>
<script>
const el = document.getElementById('sku-badge')
console.log(getComputedStyle(el).color)
</script>
</body>
</html>
建议运行:浏览器打开控制台。
期望:
计算色来自 #sku-badge(ID 特异性通常高于 class)
若去掉 id,则 .badge.badge--danger 胜于 .badge 与 span
结合场景关注点
- 不是“写在后面就一定赢”,要先比特异性。
- ID 很容易把后续覆盖成本抬爆。
- 组件变体用 class 组合,比
#id更可维护。
核心概念与准确模型
比较顺序(工程压缩)
层叠比较通常先看更宏观的条件(来源、层叠层、是否 important 等),再在同一层级内比较特异性,最后才是源顺序。完整算法以 CSS Cascading and Inheritance 为准。
特异性三元组(教学模型)
把选择器记为 (A, B, C):
| 分量 | 计入 |
|---|---|
| A | ID 选择器数量 |
| B | class、属性、伪类数量 |
| C | 类型选择器、伪元素数量 |
比较时从左到右,不是把 100/10/1 当十进制真算术(这是常见教学简化,遇到多位数会误导)。
不计入:
- 通用选择器
* - 组合符(
>+~) :where()的特异性为 0(现代好工具)- 内联
style属性具有比选择器更高的一档(另记)
!important
它改变的是声明在层叠中的优先级档位,不是“特异性 +∞ 的健康写法”。组件库与工具类冲突时尤其危险。
设计动机
CSS 允许多样式来源叠加(用户代理、作者、用户)。特异性让“更具体的意图”能覆盖“更一般的默认”,同时源顺序处理同级冲突。
边界与反直觉
- 属性选择器
[id=x]的特异性按属性选择器计,不等于#x。 - 伪类
:not()的特异性由其参数贡献(注意与:where差异)。 - 继承来的值与“选中元素的声明”比较方式不同:被继承属性没有特异性去打直接命中的声明。
常见误区
[!warning] 常见误区:用 100/10/1 做十进制加法 应逐列比较计数。11 个 class 不会进位变成 1 个 ID。
[!warning] 常见误区:靠 !important 终结战争 只会把战争抬到 important 层,维护成本更高。
工程实践
- 保持特异性平坦:BEM、CSS Modules、utility 都在降选择器深度。
- 组件状态用 class:
.is-active/.badge--danger。 - 避免 ID 选择器写样式(锚点 ID 可以有,样式别绑)。
- 调试用 DevTools 看被划掉的规则与 computed。
- Tailwind/Modules 冲突时先定位层叠层与顺序,再谈配置。
可验证实验
- 同特异性两条规则,调换源顺序,观察胜者变化。
- 给败者加
!important,观察“打赢”的代价。 - 用
:where(.badge)包一层,看特异性归零对覆盖的影响。
本节总结
特异性是层叠的标尺之一。目标不是赢得单次覆盖,而是让团队在低特异性下可预测地工作。
自测题
概念题
- 为什么说 100/10/1 是简化?
- 内联 style 与
#id谁更“硬”?
代码推理题
.a.b与#c同时命中且都无 important,谁赢?
参考答案
- 真实比较是多列计数,不会像十进制那样进位。
- 内联 style 档位通常高于 ID 选择器(除非 important 等更复杂层叠情况)。
#c的 A 分量更大,通常胜出。
延伸阅读
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| CSS Cascade 5 | 规范 | 层叠与特异性 |
| MDN Specificity | 文档 | 计算示例 |
| MDN Cascade | 文档 | 层叠全流程 |