约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证
特异性不是一个数字,而是一个三元组 (A, B, C):A 数 ID 选择器,B 数类、伪类与属性选择器,C 数元素与伪元素。比较时从左到右——A 大的赢,A 相同比 B,以此类推。把「权重 131」这种口诀忘掉,直接看三元组更不容易错。
| 选择器 | 三元组 | 说明 |
|---|---|---|
| #nav .item:hover | (1, 2, 0) | 1 个 ID + 2 个伪类/类 |
| div.card p | (0, 1, 2) | 1 类 + 2 元素,赢不过任何带 ID 的规则 |
| a:hover::before | (0, 1, 1) | :hover 计 B 位,::before 计 C 位 |
| * | (0, 0, 0) | 通配符不影响特异性,但会参与层叠顺序 |
| style="..." | 内联 | 排在所有普通规则之前,只有 !important 能压过 |
| !important | 最重要 | 同标记的重要声明之间再按三元组比较 |
伪类家族里混进了几个「规则改变者」,它们是大多数误算的来源:
/* 全部 (0,1,0)::where 里的 #app 不计权 */
:where(#app) .card { color: gray; } /* (0,1,0) */
#app .card { color: black; } /* (1,1,0) 覆盖上一行 */:is(#a, .b) p { } /* #a 是 (1,0,0),整条 = (1,0,1) */
:not(.x) p { } /* .x 是 (0,1,0),整条 = (0,1,1) */
/* 参数列表再长也只贡献一个最高值,不叠加 */在 DevTools 里按这个顺序查,90% 的案例 2 分钟内出结果:
按源码顺序:后写的赢。这也是为什么把覆盖样式放在被覆盖样式之后引入是有效的——前提是特异性相同。
普通内联样式赢过普通 !important?不——带 !important 的内联样式赢过一切,普通内联样式赢过普通规则但输给带 !important 的样式表规则。完整顺序:important 内联 > important 样式表 > 内联 > 样式表。
& 展开成父选择器后再整体计算。.card { & .title {} } 展开为 .card .title((0,2,0));若父级本身带 ID,展开后整条都带 ID。拿不准就粘进特异性计算器验证。