CSS 样式为什么不生效:特异性权重一次算清,别再堆 !important

一份可执行的特异性排查指南:(ID, 类, 元素) 三元组怎么算、:where 归零与 :is 取最大、伪元素计在哪一位、内联样式和 !important 的真实顺序,以及「写了样式却没生效」的完整排查顺序。

约 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最重要同标记的重要声明之间再按三元组比较

现代选择器的三条特殊规则

伪类家族里混进了几个「规则改变者」,它们是大多数误算的来源:

:where() 恒为 0——最安全的低优先级包装
/* 全部 (0,1,0)::where 里的 #app 不计权 */
:where(#app) .card { color: gray; }   /* (0,1,0) */
#app .card         { color: black; } /* (1,1,0) 覆盖上一行 */
:is() / :not() / :has() 取参数里最高的
:is(#a, .b) p { }   /* #a 是 (1,0,0),整条 = (1,0,1) */
:not(.x) p    { }   /* .x 是 (0,1,0),整条 = (0,1,1) */
/* 参数列表再长也只贡献一个最高值,不叠加 */

「写了样式却不生效」的排查顺序

在 DevTools 里按这个顺序查,90% 的案例 2 分钟内出结果:

  1. Elements 面板选中元素,看目标声明有没有出现在 Styles 里——没出现说明选择器根本没匹配上(拼写、层级、动态类名)
  2. 出现在 Styles 里但被划掉——点开划掉线看是谁覆盖的:同文件下方、更高特异性或另一文件
  3. 被内联样式覆盖——去掉内联,或把自己的规则升级成 !important 前先想想能不能改结构
  4. 都没问题但仍不生效——检查 !important 链:一层 important 之上还有另一层 important
  5. 还是不行——看是不是 :where 包着的低优先级全局样式,或者 CSS 嵌套里 & 展开后的真实选择器和你想的不一样

减少特异性战争的三个习惯

  • 全站约定特异性格局:单类选择器为主体((0,1,0)),状态类叠加((0,2,0)),ID 只用于 JS 钩子不进 CSS
  • 覆盖第三方样式时用 :where() 或 :is() 显式控制权重,而不是无脑堆叠祖先
  • !important 只允许出现在工具类(如 .hidden { display: none !important })——语义是「这类的优先级必须最高」

常见问题

特异性相同怎么办?

按源码顺序:后写的赢。这也是为什么把覆盖样式放在被覆盖样式之后引入是有效的——前提是特异性相同。

!important 和内联样式谁赢?

普通内联样式赢过普通 !important?不——带 !important 的内联样式赢过一切,普通内联样式赢过普通规则但输给带 !important 的样式表规则。完整顺序:important 内联 > important 样式表 > 内联 > 样式表。

CSS 嵌套里的 & 怎么算?

& 展开成父选择器后再整体计算。.card { & .title {} } 展开为 .card .title((0,2,0));若父级本身带 ID,展开后整条都带 ID。拿不准就粘进特异性计算器验证。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件