LabHub
学习 学习路径 课程

CSS — 后写的规则获胜

分三步定下来

在 LabHub 中继续学习

一句话总结

当同一属性同时匹配多条规则时,浏览器会按重要性 → 特异性 → 顺序这三个阶段选出一条。如果在前一阶段已经分出胜负,就不会再看后面的阶段。

概念图: 重要性 → 特异性 → 顺序 · “明明写了却不生效” · “如果它输了,为什么会输” · 能战胜 !important 的只有另一个 !important

为什么要记住这一点

CSS 中最耗时间的时刻,往往不是因为不懂语法,而是遇到**“明明写了却不生效”**。开发者工具中明明能看到规则,却被画上了删除线。

如果不知道原因,只是把选择器再写长一点,或再叠加一条规则蒙混过去,下一个人就必须写更长的选择器,再下一个人就会使用 !important。几个月后,文件中哪些行可以删除将无人知晓。CSS 几乎总是沿着这条路径崩坏。

理解这三个阶段,就能当场回答**“如果它输了,为什么会输”**。只要分清是特异性还是顺序导致,修复方式就会完全不同。

第一阶段——重要性

사용자 !important  >  작성자 !important  >  작성자 일반  >  사용자 일반  >  브라우저 기본

这里由 !important 获胜,所以紧急时很容易伸手使用它。但能战胜 !important 的只有另一个 !important。一旦使用,这个属性以后就只能继续用同样方式处理。这正是 CSS 崩坏的典型路径。

当你想使用 !important 时,真正的问题通常是选择器设计

第二阶段——特异性

用三位数字计算。

计算对象 示例
a ID #nav
b 类、属性、伪类 .btn[open]:hover
c 标签、伪元素 div::before
a                    → 0,0,1
.btn.primary         → 0,2,0
#nav a:hover         → 1,1,1
ul > li::marker      → 0,0,3

各位之间不会进位。 即使连接 11 个类,也无法胜过一个 ID,即 0,11,0 < 1,0,0

还有两个特殊情况。

第三阶段——顺序

这是最经常让人踩坑的阶段。

重要性和特异性都相同时,写在后面的规则获胜。 这里指的是文件中的位置,而不是它在页面中的语义。

@media (max-width: 900px) {
  .side { position: fixed; }     /* 모바일 덮개 */
}
.side { position: sticky; }      /* ← 나중에 썼다 */

这段代码在移动端也会是 sticky。位于媒体查询中并不会让规则更强。由于特异性相同(两者都是 0,1,0),后写的规则获胜。

基础规则永远要放在媒体查询之前。

这是本服务中真实发生过的错误。移动端目录遮罩无法打开,原因是基础规则 .lessonSide { position: sticky } 位于 @media 块的下方。把整个块移到上方后便修复了。

继承不是层叠

colorfont-familyline-height 等属性会被子元素继承marginpaddingdisplayborder 则不会。

继承得到的值,比任何直接作用于该元素的规则都弱。 无论直接规则的特异性多低,它都会获胜。只有“完全没有规则”时才会使用继承值。

自定义属性会继承

:root { --accent: #3b82f6; }
.btn { background: var(--accent); }

--accent 会继承,因此放在 :root 后可在任何位置使用,也能只在某个子树中覆盖

.dark-panel { --accent: #93c5fd; }   /* 이 안의 .btn 들만 색이 바뀐다 */

这正是它与预处理器变量(SCSS $)的决定性差异。SCSS 变量会在编译时消失,而自定义属性会存活到运行时,并参与层叠。这就是主题切换只需一行 JavaScript 的原因。

网格中真正容易出问题的地方

网格很容易学,但有一个知识点不知道就会造成严重问题。

设置为 display: none 的子元素会彻底退出网格。 它不会留下空位。

.layout { grid-template-columns: 0 1fr; }   /* 접었을 때 목차를 0px 로 */
.layout.hidden .side { display: none; }

原意是“把目录折叠为 0px”。但由于 display: none 让目录退出布局流,正文被推进了第一列(0px)。每个字符都另起一行,页面最终高达 33,000px。

这也是本服务中真实发生过的错误。修复只需一行。

.layout.hidden { grid-template-columns: 1fr; }   /* 칸을 아예 하나로 */

不要把列宽变为 0,而应减少列的数量。 子元素消失时,对应的列也必须消失。

什么时候用网格,什么时候用弹性布局

拿不准时可以这样问:“我是否想同时确定行和列?” 如果是,就用网格。

弹性布局中最常用的是 justify-content: space-between + align-items: center + gap 这三项。使用 margin-left: auto 把单个元素推到右侧也很常见。

现场诊断顺序

CSS “不生效”时,不要先添加规则,而应按顺序检查。

  1. 选择器是否真的匹配该元素——开发者工具中是否能看到规则
  2. 是否输给了其他规则——若有删除线,说明它输了
  3. 如果输了,为什么——因为特异性还是顺序
  4. 是否期待继承值——若有直接作用的规则,就不会使用继承

只有检查完这四项仍没有答案时,才使用 !important。通常在第三步就能结束。

实务中这套顺序之所以重要,是因为每个阶段的修复方法不同。输在特异性就应调整选择器,输在顺序就应改变文件加载顺序或规则位置。如果不区分原因而同时改动两边,就无法知道问题是真正修复了,还是只是偶然绕过去了。