Skip to content

入门

基于 CSS 现代标准 · 核于 2026-06

速查

  • 一条规则 = 选择器 + { 声明块 };声明 = 属性: 值;
  • 选择器三类基石:类型 p、类 .btn、ID #main;可叠成复合选择器 a.btn(无空格=同一元素)
  • 组合器看「关系」:后代(空格)、子 >、相邻兄弟 +、后续兄弟 ~
  • 冲突谁赢的四步铁律:来源 → 层叠层 → 特异性 → 书写顺序(前者定不了才看后者)
  • 特异性记成三列 ID-CLASS-TYPE:ID 一列 1-0-0、类/属性/伪类一列 0-1-0、类型/伪元素一列 0-0-1* 与组合器记 0
  • 平局看书写顺序:同一来源、同层、同特异性时,后写的赢
  • !important 不改特异性,而是把声明提到更高的「重要性」档,能压住普通声明——少用
  • 现代利器::has() 父选择器(Baseline 2023)、:where() 零特异性、@layer 级联层(Baseline 2022)
  • CSS 不报错:选择器没命中或值非法,浏览器静默忽略——用 DevTools 看哪条被划掉了

从一条规则说起

CSS 的最小单位是一条规则(rule):

css
/* 选择器 */     /* 声明块 */
a.button {
  color: white;        /* 声明:属性 color,值 white */
  background: navy;
}
  • 选择器a.button)回答「打中谁」——这里是带 button 类的 <a> 元素;
  • 声明块{ … })里每条 属性: 值; 回答「怎么改」。

一个页面通常有成百上千条规则,同一个元素往往被多条规则命中。于是 CSS 的两大核心问题登场:选择器负责「命中」,层叠负责「同一属性被多条规则赋值时,最终听谁的」。

第一问:选择器怎么命中

最常用的三种基础选择器,按精度递增:

css
p {
  /* 类型选择器:所有 <p> */
}
.note {
  /* 类选择器:所有 class 含 note 的元素 */
}
#sidebar {
  /* ID 选择器:id="sidebar" 的那一个 */
}

把它们贴在一起、不留空格,就是「同时满足」的复合选择器:

css
a.button {
  /* 既是 <a>、又有 button 类 */
}

空格或组合符,则表达元素之间的关系

css
nav a {
  /* 后代:<nav> 内任意层级的 <a> */
}
nav > a {
  /* 子:只命中 <nav> 的直接子 <a> */
}
h2 + p {
  /* 相邻兄弟:紧跟在 <h2> 后的那个 <p> */
}
h2 ~ p {
  /* 后续兄弟:<h2> 之后的所有同级 <p> */
}

选择器还有属性选择器([type="email"])、伪类(:hover:first-child)、伪元素(::before)等一大家族——全谱见 选择器家族伪类与伪元素

第二问:冲突了听谁的

来看一个会「打架」的例子:

html
<a class="button" id="cta" href="#">点我</a>
css
a {
  color: blue;
} /* 规则 1 */
.button {
  color: green;
} /* 规则 2 */
#cta {
  color: red;
} /* 规则 3 */

三条规则都给同一个 <a>color 赋了值,最终它是红色。决定权来自 CSS 的层叠算法,它依次比较四个维度,前一维分不出胜负才看下一维

  1. 来源(origin):样式来自浏览器默认、用户、还是你写的作者样式;
  2. 层叠层(@layer:作者样式内部,按层的先后排座次(见 级联层实战);
  3. 特异性(specificity):选择器越「具体」越优先——ID 比类强,类比类型强;
  4. 书写顺序(order):以上全平时,后写的赢

上例三条规则同来源、同层,于是看特异性#cta 是 ID(1-0-0)> .button 是类(0-1-0)> a 是类型(0-0-1),ID 那一列最大,红色胜出。

把特异性想成三列计分,而非一个总分

特异性写成 ID-CLASS-TYPE 三列(如 1-0-2),从左往右逐列比大小,左列只要更大就直接赢——并非把各列加成一个数。所以一个 ID(1-0-0)能压过十几个类(0-11-0)。详见 特异性计算

!important 与它的代价

如果想让 .button 强行压过 ID,很多人第一反应是 !important

css
.button {
  color: green !important;
}

!important 不提升特异性,而是把这条声明挪进一个更高的「重要性」档位——普通声明再特异也压不过带 !important 的声明。它确实能赢,但代价是:下次你想再覆盖它,只能用另一个 !important,于是 !important 像滚雪球一样越堆越多,层叠彻底失控。

现代 CSS 给了更好的解法:用 @layer 把第三方 / 工具类样式放进较低的层,你的普通样式自然就能覆盖它们,无需 !important(见 级联层实战);写基础样式时用 :where() 把特异性压到 0,让业务样式轻松覆盖(见 特异性计算)。

CSS 不会报错——用 DevTools 看真相

和 JS 不同,CSS 遇到拼错的选择器非法的值不会抛错、不会中断,只是静默忽略那一条。所以「样式没生效」时别干瞪代码,打开浏览器 DevTools 的 Elements/Styles 面板:

  • 命中该元素的所有规则会列出来,被覆盖的声明带删除线——一眼看出谁赢了、被谁压了;
  • 如果你期望的规则根本没出现在列表里,说明选择器没匹配上(多半是拼写或语法问题)。
css
/* 例:值非法,整条 color 声明被静默丢弃,元素仍用继承来的颜色 */
.box {
  color: #zzz; /* 不是合法颜色 → 被忽略,不报错 */
}

一张图理清全局

选择器  →  命中哪些元素


层叠算法 →  多条规则给同一属性赋值时决出胜者
   ① 来源(UA < 用户 < 作者;!important 反转)
   ② 层叠层(@layer:后声明的层赢;未分层 > 已分层)
   ③ 特异性(ID-CLASS-TYPE 逐列比)
   ④ 书写顺序(后写的赢)


没有任何规则命中该属性?
   → 继承(color/font 等会继承)或用属性的初始值兜底

下一步

地基已经打好。接下来从「命中」这一侧深入——先看选择器全谱:选择器家族