Skip to content

✨ CSS 层叠 👌

1. 层叠概述

层叠(Cascade) 是 CSS 的核心机制之一,用于解决样式声明冲突的问题。

1.1 什么是层叠

层叠是浏览器决定当多个 CSS 规则应用到同一个元素时,哪个规则最终生效的过程。它通过一套严格的优先级规则来自动处理样式冲突。

1.2 层叠的重要性

  • 解决冲突:当多个样式规则作用于同一元素时,确定最终应用的样式
  • 可预测性:提供一套明确的规则,让开发者能够预测样式的最终效果
  • 灵活性:允许样式的覆盖和继承,提高代码的可维护性

2. 声明冲突

2.1 什么是声明冲突

声明冲突:同一个 CSS 属性,通过不同的选择器多次应用到同一个元素上。

2.2 冲突产生的场景

html
<!DOCTYPE html>
<html>
  <head>
    <style>
      /* 场景1:不同选择器设置相同属性 */
      p {
        color: blue;
      }
      .text {
        color: red;
      }
      #content {
        color: green;
      }

      /* 场景2:同一选择器重复声明 */
      .box {
        width: 100px;
        width: 200px; /* 后面的会覆盖前面的 */
      }

      /* 场景3:外部样式表与内部样式冲突 */
      h1 {
        font-size: 24px;
      }
    </style>
    <link rel="stylesheet" href="external.css" />
  </head>
  <body>
    <p class="text" id="content">这段文字的颜色是什么?</p>
    <div class="box">这个盒子的宽度是多少?</div>
  </body>
</html>

2.3 冲突解决机制

浏览器通过层叠算法来解决冲突,按照以下顺序进行比较:

  1. 重要性(Importance)
  2. 特殊性(Specificity)
  3. 源次序(Source Order)

3. 重要性比较

3.1 重要性层级

重要性从高到低的顺序:

  1. 作者样式表中的 !important 样式
  2. 作者样式表中的普通样式
  3. 浏览器默认样式表中的样式

3.2 样式表类型详解

3.2.1 作者样式表(Author Stylesheet)

开发者编写的样式,包括:

css
/* 内联样式 */
<div style="color: red;">内联样式</div>

/* 内部样式表 */
<style>
    .text { color: blue; }
</style>

/* 外部样式表 */
<link rel="stylesheet" href="style.css">

3.2.2 用户代理样式表(User Agent Stylesheet)

浏览器的默认样式:

css
/* 浏览器默认样式示例 */
h1 {
  display: block;
  font-size: 2em;
  margin-top: 0.67em;
  margin-bottom: 0.67em;
  font-weight: bold;
}

p {
  display: block;
  margin-top: 1em;
  margin-bottom: 1em;
}

4. 特殊性比较

4.1 特殊性计算规则

特殊性通过四位数字 (a, b, c, d) 来表示:

位置权重计算规则示例
a千位内联样式数量style="color: red"
b百位ID 选择器数量#header
c十位类、属性、伪类选择器数量.btn, [type="text"], :hover
d个位元素、伪元素选择器数量div, ::before

4.2 特殊性计算示例

css
/* 示例1:(0, 0, 0, 1) */
p {
  color: black;
}

/* 示例2:(0, 0, 1, 0) */
.text {
  color: blue;
}

/* 示例3:(0, 1, 0, 0) */
#title {
  color: red;
}

/* 示例4:(0, 0, 1, 1) */
p.text {
  color: green;
}

/* 示例5:(0, 1, 1, 1) */
#title p.text {
  color: purple;
}

/* 示例6:(0, 0, 2, 2) */
div.container p.text {
  color: orange;
}

/* 示例7:(0, 0, 1, 0) */
[class="text"] {
  color: yellow;
}

/* 示例8:(0, 0, 2, 0) */
.text:hover {
  color: pink;
}

/* 示例9:(0, 0, 0, 2) */
div::before {
  content: "";
}

/* 示例10:(1, 0, 0, 0) */
/* <div style="color: red;"> 内联样式 */

5.3 复杂选择器计算

css
/* 复杂示例分析 */

/* (0, 1, 1, 3) */
#header .nav ul li a {
  /* 1个ID + 1个类 + 3个元素 */
}

/* (0, 0, 4, 2) */
.sidebar .menu li:first-child a:hover {
  /* 2个类 + 2个伪类 + 2个元素 */
}

/* (0, 2, 1, 1) */
#header #nav .logo span {
  /* 2个ID + 1个类 + 1个元素 */
}

5.4 特殊性比较规则

css
/* 比较示例 */
.text {
  color: blue;
} /* (0, 0, 1, 0) */
p {
  color: red;
} /* (0, 0, 0, 1) */
/* 结果:蓝色胜出,因为 (0, 0, 1, 0) > (0, 0, 0, 1) */

#title {
  color: green;
} /* (0, 1, 0, 0) */
.text.highlight {
  color: yellow;
} /* (0, 0, 2, 0) */
/* 结果:绿色胜出,因为 (0, 1, 0, 0) > (0, 0, 2, 0) */

::: important

重要:特殊性比较是逐位比较,不是数值相加!

:::

5.5 通配符和组合器

css
/* 通配符选择器特殊性为 0 */
* {
  color: black;
} /* (0, 0, 0, 0) */

/* 组合器不增加特殊性 */
div > p {
  color: blue;
} /* (0, 0, 0, 2) */

div + p {
  color: red;
} /* (0, 0, 0, 2) */

div ~ p {
  color: green;
} /* (0, 0, 0, 2) */

/* :not() 伪类本身不计算,但其参数计算 */
:not(.text) {
  color: yellow;
} /* (0, 0, 1, 0) */

6. 源次序比较

6.1 源次序规则

当重要性和特殊性都相同时,后声明的样式会覆盖先声明的样式

css
/* 示例:相同特殊性的规则 */
.text {
  color: red;
} /* 先声明 */

.text {
  color: blue;
} /* 后声明,会覆盖红色 */

p {
  font-size: 14px;
} /* 先声明 */

p {
  font-size: 16px;
} /* 后声明,会覆盖 14px */

6.2 样式表加载顺序

html
<!DOCTYPE html>
<html>
  <head>
    <!-- 1. 第一个外部样式表 -->
    <link rel="stylesheet" href="reset.css" />

    <!-- 2. 第二个外部样式表 -->
    <link rel="stylesheet" href="main.css" />

    <!-- 3. 内部样式表 -->
    <style>
      .text {
        color: blue;
      }
    </style>
  </head>
  <body>
    <!-- 4. 内联样式(特殊性最高) -->
    <p class="text" style="color: red;">文本内容</p>
  </body>
</html>

6.3 爱恨法则(LVHA)

链接伪类的正确顺序:

css
/* 正确的顺序:LoVe HAte */
a:link {
  color: blue;
} /* L - Link */
a:visited {
  color: purple;
} /* V - Visited */
a:hover {
  color: red;
} /* H - Hover */
a:active {
  color: orange;
} /* A - Active */

7. 实际应用

7.1 重置样式表

7.1.1 什么是重置样式表

重置样式表用于覆盖浏览器的默认样式,确保在不同浏览器中获得一致的显示效果。

7.1.2 常见的重置样式表

1. Reset.css(激进重置)

css
/* Eric Meyer's Reset CSS */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}

2. Normalize.css(温和重置)

css
/* Normalize.css 部分示例 */
html {
  line-height: 1.15;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
}

h1 {
  font-size: 2em;
  margin: 0.67em 0;
}

button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}

3. 自定义重置样式

css
/* 现代化的重置样式 */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  font-size: 16px;
  line-height: 1.5;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: #333;
  background-color: #fff;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 1rem 0;
  font-weight: bold;
  line-height: 1.2;
}

p {
  margin: 0 0 1rem 0;
}

ul,
ol {
  margin: 0 0 1rem 0;
  padding-left: 2rem;
}

img {
  max-width: 100%;
  height: auto;
}

button {
  cursor: pointer;
  border: none;
  background: none;
  font: inherit;
}

7.1.3 重置样式表的使用

html
<!DOCTYPE html>
<html>
  <head>
    <!-- 1. 首先加载重置样式表 -->
    <link rel="stylesheet" href="reset.css" />

    <!-- 2. 然后加载自定义样式 -->
    <link rel="stylesheet" href="main.css" />
  </head>
  <body>
    <!-- 页面内容 -->
  </body>
</html>

7.2 组件样式设计

按钮组件示例

css
/* 基础按钮样式 */
.btn {
  display: inline-block;
  padding: 0.5rem 1rem;
  border: 1px solid transparent;
  border-radius: 0.25rem;
  font-size: 1rem;
  line-height: 1.5;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: all 0.3s ease;
}

/* 主要按钮 */
.btn-primary {
  color: white;
  background-color: #007bff;
  border-color: #007bff;
}

.btn-primary:hover {
  background-color: #0056b3;
  border-color: #004085;
}

/* 次要按钮 */
.btn-secondary {
  color: white;
  background-color: #6c757d;
  border-color: #6c757d;
}

/* 大尺寸按钮 */
.btn-lg {
  padding: 0.75rem 1.5rem;
  font-size: 1.25rem;
}

/* 小尺寸按钮 */
.btn-sm {
  padding: 0.25rem 0.5rem;
  font-size: 0.875rem;
}

8. 最佳实践建议

8.1 层叠管理策略

8.1.1 避免过度使用 !important

css
/* 不推荐:滥用 !important */
.bad-practice {
  color: red !important;
  font-size: 16px !important;
  margin: 10px !important;
}

/* 推荐:合理的选择器设计 */
.component .header .title {
  color: red;
  font-size: 16px;
  margin: 10px;
}

8.1.2 使用合理的选择器特殊性

css
/* 不推荐:特殊性过高 */
#header #nav #menu .item .link {
  color: blue;
}

/* 推荐:适中的特殊性 */
.nav-link {
  color: blue;
}

.nav-link.active {
  color: red;
}

8.2 代码组织原则

8.2.1 样式表加载顺序

html
<!-- 推荐的加载顺序 -->
<head>
  <!-- 1. 重置样式 -->
  <link rel="stylesheet" href="reset.css" />

  <!-- 2. 第三方库 -->
  <link rel="stylesheet" href="bootstrap.css" />

  <!-- 3. 基础样式 -->
  <link rel="stylesheet" href="base.css" />

  <!-- 4. 组件样式 -->
  <link rel="stylesheet" href="components.css" />

  <!-- 5. 页面特定样式 -->
  <link rel="stylesheet" href="page.css" />
</head>

8.2.2 CSS 架构方法

1. BEM 方法论

css
/* Block Element Modifier */
.card {
} /* Block */
.card__header {
} /* Element */
.card__body {
} /* Element */
.card--large {
} /* Modifier */
.card__header--highlighted {
} /* Element + Modifier */

2. OOCSS 原则

css
/* 结构与皮肤分离 */
.btn {
  /* 结构样式 */
  display: inline-block;
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
}

.btn-primary {
  /* 皮肤样式 */
  background-color: #007bff;
  color: white;
}

.btn-secondary {
  /* 皮肤样式 */
  background-color: #6c757d;
  color: white;
}

8.3 性能考虑

8.3.1 CSS 文件优化

css
/* 合并相似规则 */
.btn-primary,
.btn-secondary,
.btn-success {
  /* 共同样式 */
  display: inline-block;
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
}

/* 使用 CSS 变量减少重复 */
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
  --border-radius: 0.25rem;
}

.btn {
  border-radius: var(--border-radius);
}