CSS 基础
约 3152 字大约 11 分钟
2025-11-06
层叠
在开发当中,我们都遇到过这种苦恼:为什么这个样式不生效?为什么这个样式不起作用?
当遇到这种规则没有按照预期生效时,可能是因为它与另一条规则冲突了。要想预测规则最终的效果,就需要理解 CSS 里的层叠。
当一些规则指向了同一个元素时,就会存在冲突的声明。浏览器为了解决这个问题会遵循一系列规则, 层叠指的就是这一系列规则。它决定了如何解决冲突,是 CSS 语言的基础。
当声明冲突时,层叠会依据三种条件解决冲突。
(1) 样式表的来源:样式是从哪里来的,包括你的样式和浏览器默认样式等。
(2) 选择器优先级:哪些选择器比另一些选择器更重要。
(3) 源码顺序:样式在样式表里的声明顺序。
样式表的来源
当你写一些标签不加任何样式时会发现如:<h1> 有默认 padding 和 margin、<a> 有默认的文字颜色..., 这些样式都是浏览器默认样式,来源于用户代理样式。
用户代理样式在不同浏览器上稍有差异,但是大体上是在做相同的事情:为标题<h1>到<h6>和段落<p>添加上下外边距,为列表<ol>和<ul>添加左侧内边距,为链接添加颜色,为元素设置各种默认字号。我们自己写的样式 - 作者样式表可以覆盖用户代理样式,因为它们有更高的优先级。
选择器优先级
选择器的优先级从高到低如下:
- 行内样式样式
- id 选择器
- 类选择器、伪类选择器、属性选择器
- 标签选择器
这几种选择器单独使用时很容易判断优先级,但是当多种选择器组合起来时,我们如何确定优先级呢?
《深入解析 CSS》中介绍了一种优先级标记的方法,来判断哪个选择器优先级更高。
用一个 n,n,n 的数值形式标气选择器。第一个数值表示 ID 选择器个数,第二个数值表示类选择器个数,第三个数值表示标签选择器个数。 优先级最高的 ID 列为第一位,紧接着是类,最后是标签。
#app .container .head h1 h2 h3 {
...;
}上方的选择器优先级为 1,2,3,因为 id 选择器个数为 1,类选择器个数为 2,标签选择器个数为 3。将选择器用这种方法进行标记, 第一位优先级最高,第二位其次,第三位最低,就可以像比较三位数一样轻松判断优先级了。 例如 “1.5.2” 的优先级使用比 “0,8,5” 更高。
提示
行内元素属于“带作用域的”声明,它会覆盖任何来自样式表或者 <style> 标签的样式(带有 !important 除外)。
伪类选择器(如 :hover)、属性选择器(如[type="button"])、类选择器优先级相同。通用选择器(*)和组合器(>、+、~)对优先级没有影响。
优先级提升
从上面的内容我们知道,选择器是有优先级的。因此我们可以提高选择器的优先级,让其覆盖更低优先级的样式。
<div class="card">
<h1 class="title">这是卡片标题</h1>
</div>.card h1 {
color: red;
.card .title {
color: blue;
}上面的示例中,.card h1 的优先级为 “0,1,1” ,.card .title 的优先级为 “0,2,0”,因此 .card .title 样式会覆盖 .card h1 样式,文字将显示蓝色。
其实经验告诉我们,选择器的指向越清晰,优先级则更高。
除此之外,提升优先级还有一种方法,就是使用 !important 。使用 !important 会使样式的优先级提升到最高,甚至覆盖行内样式。 但使用 !important 会绕过叠层机制,导致样式难以预测和维护。
一旦使用了 !important,后续想覆盖这个样式必须再使用 !important,这样导致的结果就是 important 越来越多,样式优先级将会越来越高。
源码顺序
如果两个声明的来源和优先级相同,其中一个声明在样式表中出现较晚,或者位于页面较晚引入的样式表中,则该声明的优先级更高。
总结
浏览器则根据以上三个步骤,即来源、优先级、源码顺序,来解析网页上每个元素的每个属性。
在处理层叠时有两个重要经验:
- 在选择器中不要使用 ID。就算只用一个 ID,也会大幅提升优先级。当需要覆盖这个选择器时,通常找不到另一个有意义的 ID,于是就会复制原来的选择器, 然后加上另一个类,让它区别于想要覆盖的选择器。
- 在选择器中不要使用 ID。就算只用一个 ID,也会大幅提升优先级。当需要覆盖这个选择器时,通常找不到另一个有意义的 ID, 于是就会复制原来的选择器,然后加上另一个类,让它区别于想要覆盖的选择器。
思考: 下面的例子,文字 “这是卡片标题” 将会显示什么颜色?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
</head>
<body>
<div class="card">
<h1 style="color: pink" class="title">这是卡片标题</h1>
</div>
</body>
</html>.card h1 {
color: red !important;
.card h1 {
color: orange !important;
.card h1 {
color: green;
.card .title {
color: blue;
}答案:橙色
继承
概述
继承是指如果一个元素的某个属性没有层叠值 ,则可能会继承某个祖先元素的值。
但不是所有的属性都能被继承。默认情况下,只有特定的一些属性能被继承,通常是我们希望被继承的那些。 它们主要是跟文本相关的属性:color、font、font-family、font-size、font-weight、font-variant(控制小型大写字母)、font-style、line-height、 letter-spacing(设置字符间距)、text-align、text-indent(设置段落首行缩进)、text-transform(控制文本大小写转换)、white-space以及word-spacing。
还有一些其他的属性也可以被继承,比如列表属性:list-style、list-style-type、list-style-position以及list-style-image。 表格的边框属性border-collapse(控制表格边框是否“合并”为单一边框还是保持分离)和border-spacing(当 border-collapse: separate 时,设置单元格边框之间的间距)也能被继承。
特殊值
我们可以将 inherit 和 initial 特殊值赋给任意属性。
如果想用继承代替一个层叠值,用inherit关键字,这样该元素就会继承其父元素的值。如果想要撤销某个属性,可以使用 initial 关键字,这样该元素就会使用它的默认值。
注意:默认值是该属性的默认值,而不是元素的默认值。例如:color 属性的默认值是 black,display 的默认值是 inline。
简写属性
某些属性可以简写,例如:font 可以用于设置多种字体属性 - font-style、font-weight、font-size、font-height以及font-family。
使用简写属性,需要注意两点:忽略掉未设置的属性,以及属性的顺序。
忽略掉未设置的属性
我们使用简写时,通常可以省略一些我们不想设置的属性。但是要知道,这样做会导致那些没有设置的值会被覆盖为默认值。
h1 {
font-weight: 900;
}
h1 {
font: 32px Arial, sans-serif;
}若是用标签 <h1> ,将不会加粗。因为 font 属性被设置为 32px Arial, sans-serif,忽略了 font-weight 的值,被设置为默认值 400。
简写值的顺序
简写属性会尽量包容指定的属性值的顺序。可以设置border: 1px solid black或者border: black 1px solid,两者都会生效。 这是因为浏览器知道宽度、颜色、边框样式分别对应什么类型的值。
但是有很多属性的值很模糊。在这种情况下,值的顺序很关键。理解这些简写属性的顺序很重要。
- 上右下左
像 padding 和 margin 属性,是按顺时针方向,从上边开始的 - 上,右,下,左。
如果声明结束时四个属性值还剩一个没指定,没有指定的一边会取其对边的值。 指定三个值时,左边和右边都会使用第二个值。 指定两个值时,上边和下边会使用第一个值。如果只指定一个值,那么四个方向都会使用这个值。例如 padding: 10px 20px; 相当于 padding: 10px 20px 10px 20px;, padding: 10px 20px 30px; 相当于 padding: 10px 20px 30px 20px;
- 水平垂直
有一些属性,最多指定两个值,例如:background-position、box-shadow、text-shadow。这些属性的顺序则不在是上右下左,而是先水平再垂直。
单位
px
- 绝对单位(相对于屏幕的物理像素,在大多数现代浏览器中是固定的)。
- 不随字体大小或用户缩放而改变(尽管浏览器缩放时整体页面会放大,但
px本身不会动态响应)。 - 适合需要精确控制尺寸的场景,比如边框、图标大小等。
.element {
font-size: 16px;
padding: 10px;
}em
- 相对单位,相对于当前元素的字体大小(如果当前元素未设置,则继承父元素的
font-size)。 - 具有继承性和累积性:嵌套使用时容易产生意外的缩放(例如,子元素 1em = 父元素 font-size × 1)。
- 适合用于组件内部的相对布局,比如 padding、margin 与文字大小成比例。
.parent {
font-size: 20px;
}
.child {
font-size: 0.8em; /* = 16px */
margin: 2em; /* = 32px */
}rem
- 相对单位,始终相对于根元素(
<html>)的font-size。 - 不会受父元素影响,避免了
em的嵌套放大/缩小问题。 - 非常适合全局响应式设计,比如通过媒体查询调整根字体大小,整个页面按比例缩放。
html {
font-size: 16px; /* 可在不同屏幕尺寸下动态调整 */
}
.component {
font-size: 1.25rem; /* = 20px */
padding: 0.5rem; /* = 8px */
}使用:
- 排版、间距、容器尺寸:优先使用
rem,便于整体缩放和无障碍支持。 - 组件内部微调(如按钮内边距与文字对齐):用
em更自然。 - 固定尺寸元素(如 1px 边框、固定宽高的图标):用
px保证精确性。 - 响应式设计:可结合
rem+media query控制根字体大小,实现响应式缩放。
视口单位
vw:视口宽度的 1/100。vh:视口高度的 1/100。vmin:视口宽高中较小一边。vmax:视口宽高中较大一边。
视口区域是指网页里的可见区域,而不包括浏览器的标题栏、工具栏、地址栏。
一个冷门的用途
如果想要字体随着窗口大小平滑的缩放,可以使用 vw 与 em 结合设置字体大小。em 保证最小值,vw 保证平滑缩放。
h1{
font-size: calc(0.5em + 1vw);
}自定义属性
CSS 自定义属性(也称为 CSS 变量)是一种强大的原生 CSS 机制,允许定义可复用的值,并在样式表中动态引用和更新。
语法
1. 以 -- 开头定义
:root {
--primary-color: #3b82f6;
--border-radius: 8px;
--space-md: 1rem;
}2. 通过 var() 函数使用
.button {
background-color: var(--primary-color);
border-radius: var(--border-radius);
padding: var(--space-md);
}3. 提供备用值
.title {
color: var(--text-color, #333); /* 若 --text-color 未定义,则用 #333 */
}全局与局部
- CSS 自定义属性遵循 CSS 继承规则。
- 在元素局部定义,后代子元素继承。
- 在
:root中定义的变量,所有元素都会继承。
.card {
--text-p: orange;
}
.title {
color: var(--text-p);
}<div class="card">
<h1 class="title">这是卡片标题</h1>
</div>动态更新
可通过 JS 动态修改,实现主题切换或交互反馈:
// 修改全局变量
document.documentElement.style.setProperty('--primary-color', '#ef4444');
// 修改组件内变量(假设组件根元素 ref 为 cardRef)
cardRef.style.setProperty('--card-bg', '#f0f9ff');