入门
基于 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):
/* 选择器 */ /* 声明块 */
a.button {
color: white; /* 声明:属性 color,值 white */
background: navy;
}- 选择器(
a.button)回答「打中谁」——这里是带button类的<a>元素; - 声明块(
{ … })里每条属性: 值;回答「怎么改」。
一个页面通常有成百上千条规则,同一个元素往往被多条规则命中。于是 CSS 的两大核心问题登场:选择器负责「命中」,层叠负责「同一属性被多条规则赋值时,最终听谁的」。
第一问:选择器怎么命中
最常用的三种基础选择器,按精度递增:
p {
/* 类型选择器:所有 <p> */
}
.note {
/* 类选择器:所有 class 含 note 的元素 */
}
#sidebar {
/* ID 选择器:id="sidebar" 的那一个 */
}把它们贴在一起、不留空格,就是「同时满足」的复合选择器:
a.button {
/* 既是 <a>、又有 button 类 */
}留空格或组合符,则表达元素之间的关系:
nav a {
/* 后代:<nav> 内任意层级的 <a> */
}
nav > a {
/* 子:只命中 <nav> 的直接子 <a> */
}
h2 + p {
/* 相邻兄弟:紧跟在 <h2> 后的那个 <p> */
}
h2 ~ p {
/* 后续兄弟:<h2> 之后的所有同级 <p> */
}选择器还有属性选择器([type="email"])、伪类(:hover、:first-child)、伪元素(::before)等一大家族——全谱见 选择器家族 与 伪类与伪元素。
第二问:冲突了听谁的
来看一个会「打架」的例子:
<a class="button" id="cta" href="#">点我</a>a {
color: blue;
} /* 规则 1 */
.button {
color: green;
} /* 规则 2 */
#cta {
color: red;
} /* 规则 3 */三条规则都给同一个 <a> 的 color 赋了值,最终它是红色。决定权来自 CSS 的层叠算法,它依次比较四个维度,前一维分不出胜负才看下一维:
- 来源(origin):样式来自浏览器默认、用户、还是你写的作者样式;
- 层叠层(
@layer):作者样式内部,按层的先后排座次(见 级联层实战); - 特异性(specificity):选择器越「具体」越优先——ID 比类强,类比类型强;
- 书写顺序(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:
.button {
color: green !important;
}!important 不提升特异性,而是把这条声明挪进一个更高的「重要性」档位——普通声明再特异也压不过带 !important 的声明。它确实能赢,但代价是:下次你想再覆盖它,只能用另一个 !important,于是 !important 像滚雪球一样越堆越多,层叠彻底失控。
现代 CSS 给了更好的解法:用 @layer 把第三方 / 工具类样式放进较低的层,你的普通样式自然就能覆盖它们,无需 !important(见 级联层实战);写基础样式时用 :where() 把特异性压到 0,让业务样式轻松覆盖(见 特异性计算)。
CSS 不会报错——用 DevTools 看真相
和 JS 不同,CSS 遇到拼错的选择器或非法的值不会抛错、不会中断,只是静默忽略那一条。所以「样式没生效」时别干瞪代码,打开浏览器 DevTools 的 Elements/Styles 面板:
- 命中该元素的所有规则会列出来,被覆盖的声明带删除线——一眼看出谁赢了、被谁压了;
- 如果你期望的规则根本没出现在列表里,说明选择器没匹配上(多半是拼写或语法问题)。
/* 例:值非法,整条 color 声明被静默丢弃,元素仍用继承来的颜色 */
.box {
color: #zzz; /* 不是合法颜色 → 被忽略,不报错 */
}一张图理清全局
选择器 → 命中哪些元素
│
▼
层叠算法 → 多条规则给同一属性赋值时决出胜者
① 来源(UA < 用户 < 作者;!important 反转)
② 层叠层(@layer:后声明的层赢;未分层 > 已分层)
③ 特异性(ID-CLASS-TYPE 逐列比)
④ 书写顺序(后写的赢)
│
▼
没有任何规则命中该属性?
→ 继承(color/font 等会继承)或用属性的初始值兜底下一步
地基已经打好。接下来从「命中」这一侧深入——先看选择器全谱:选择器家族。