层叠(Cascade)= 冲突时有个裁判

浏览器对每个元素的每个属性只保留一条获胜声明。裁判的判序是:① 来源与重要性 → ② 层(@layer)→ ③ 权重 → ④ 书写顺序。前三步比不出才看谁写在后面。

权重计算:三位数字

成分来自数量级
id 的个数#buy1,0,0
class / 属性 / 伪类的个数.btn、[type=text]、:hover、:not() 内部的参数0,1,0
类型(元素名)与伪元素的个数p、::before0,0,1

下面三条规则都命中同一个按钮,最终是红色:

.page .btn        { color: #8250df; }   /* 0,2,0 */
.btn.primary      { color: #0969da; }   /* 0,2,0 —— 与上一条同权重,写在后面才胜出 */
#buy.primary      { color: #cf222e; }   /* 1,1,0 ← 赢 */

立即购买

三个常见误解:① 权重不是十进制,11 个 class 也压不过 1 个 id(0,11,0 vs 1,0,0);② * 和组合子(> + ~ 空格)权重为 0;③ :where() 权重为 0,而 :is() 取参数里最高的那个(第十五章)。元素的 style="" 不参与这套计数,它比所有规则都高(除了 !important)。

!important 的代价

.imp-a { color: #57606a; }
.imp-b { color: #1a7f37 !important; }
#hardcoded { color: #0969da; }   /* 权重 1,0,0 仍然输给带 !important 的 .imp-b */

这一行是绿色:!important 赢了权重

用了它,唯一的反制手段是更具体的选择器 + 另一个 !important,或者用内联 style 加 !important。项目里出现「为了盖住一行 !important 再加一行 !important」就说明已经失控。合理的用法只有三类:第三方样式表的临时补丁、工具类(如 .visually-hidden)、用户样式表(无障碍场景)。

继承:只有「与文字相关」的大多数属性会传

这一层设了 color:#cf222e、等宽字体、line-height:1.9。

子元素没写任何 color,但它是红的、等宽的、行高 1.9 → color/font-*/line-height/text-align/visibility/cursor 都继承。

这里写了 color: initial → 回到该属性的初始值(黑),继承被切断。

继承不继承
color、font-*、line-height、text-*、letter-spacing、visibility、cursor、writing-mode盒模型(margin/padding/border/width/height)、background、display、position 与定位、flex/grid 全部、overflow、opacity、transform
/* 四个关键字,随时手动改变继承行为 */
color: inherit;   /* 强制继承父元素的值 */
color: initial;   /* 强制回到该属性的初始值 */
color: unset;     /* 可继承属性 = inherit,不可继承属性 = initial */
color: revert;    /* 回到「上一层来源」的值,通常是浏览器默认样式 */

revert 与 unset 的区别很实用:浏览器给 ul 的默认 padding-inline-start: 40px,写 unset 得到 0,写 revert 也是回到默认——但当你想「恢复到用户代理样式表」或「撤销用户自己的覆盖样式」时,只有 revert 正确。另有 revert-layer 撤销当前 @layer 的层内结果(第十六章)。

为什么「后写的」没生效:四条排查思路

  1. 看 DevTools Styles 面板里那条规则有没有被划掉:划掉说明被更高权重或 !important 压制,面板会直接标出是谁压的。
  2. 规则根本没出现在面板里 → 选择器没命中(拼错、层级不对)或整块解析失败。
  3. 在 Computed 面板搜属性名,能看到最终值的来源声明。
  4. 怀疑是继承:给元素显式写 color: initial 试一下,值变了说明原来那个值是继承来的。