【全篇高达13万字】CSS 深度修炼:从基础到进阶,成为网页样式设计大师的独家笔记


序章:掌握 CSS,构建专业网页外观

摘要: 本笔记将直面初学者在学习和使用 CSS 时普遍感到的困惑与挫败感,特别是在与 AI 工具协作时,样式代码的维护性问题尤为突出。我们将深入分析编写混乱的 CSS 代码所带来的具体痛点,并由此引出本系列教程的核心主张:通过系统学习 CSS 的核心基础,从第一天起就建立编写专业、可扩展、易维护样式代码的思维模式与工程能力。


在本章中,我们将循序渐进,清晰地剖析问题的根源,并为您铺平学习 CSS 的道路:

  1. 首先,我们将深入 痛点分析,复现那些令初学者头疼的真实场景,让您感同身受。
  2. 接着,我们将明确 学习路径,告诉您掌握 CSS 的关键在于理解其内在的核心法则。
  3. 然后,我们将手把手指导您完成 环境搭建,一个代码编辑器和一个浏览器足矣。
  4. 最后,我们将通过 首次实践,让您亲手链接第一张样式表,看到代码生效的即时反馈。

0.1. 痛点分析:为何传统 CSS 难以维护?

业务背景: 假设你是一位刚掌握 HTML 的前端新人,正尝试独立或在 AI 的辅助下,为一个个人信息卡片编写样式。卡片结构很简单,包含头像、姓名、职位和社交链接。

你的第一个版本的样式代码可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* style.css */
body {
background-color: #f0f2f5;
font-family: sans-serif;
}

.user-card {
width: 300px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
margin: 50px auto;
padding: 20px;
}

.user-card .avatar {
width: 80px;
height: 80px;
border-radius: 50%;
display: block;
margin-left: auto;
margin-right: auto;
}
/* ...其他样式... */

这段代码能够正常工作,但随着项目变得复杂,或者当你需要修改 AI 生成的类似代码时,以下几个核心痛点会逐渐暴露:

开发者困境
2025-08-25

为什么 AI 生成的一大段 CSS,我只想改个主题色和圆角大小,却要改好几个地方?

P
Prorise

这是因为代码中缺少对“设计规范”的抽象。颜色值、尺寸值等“魔术数字”被硬编码在各处,缺乏统一管理的入口。

为什么我的样式越写越多,不同组件的样式会互相“打架”?比如我在别处定义的 .title 样式影响到了卡片里的标题。

P
Prorise

这是因为对 CSS 选择器的 全局作用域和优先级机制 理解不足,导致了样式污染和意料之外的覆盖。

为什么我想复用一个居中布局的样式,却只能到处复制粘贴相同的几行代码?

P
Prorise

这是因为没有运用 CSS 的 组合和复用能力,例如通过创建通用的工具类来封装常用样式。

HTML 结构是层层嵌套的,为什么我的 CSS 却要写一长串的选择器,看起来非常扁平,无法体现结构?

P
Prorise

这是因为未能有效利用 组合选择器 来表达 DOM 结构,导致选择器变得冗长且脆弱。

核心问题: CSS 本身是一门强大而灵活的语言,但若缺乏对其核心规则(如选择器、层叠、盒模型)的深刻理解,代码将不可避免地走向混乱。解决方案并非逃避 CSS,而是掌握它。


0.2. 解决方案:回归基础,掌握 CSS 核心

要解决上述所有痛点,我们无需学习额外的工具,只需回归 CSS 本身,系统地掌握其内在的几大核心支柱。本教程将围绕这些支柱展开:

  • 选择器: 这是与 HTML “对话”的基础。我们将学习如何精准地选中任何你想要添加样式的元素。
  • 层叠与优先级: 这是解决样式冲突的关键。我们将学习浏览器如何决定哪条规则最终生效。
  • 盒模型: 这是所有网页布局的原子。我们将学习元素如何由内容、内边距、边框和外边距构成。
  • 显示与布局: 这是控制元素排列的艺术。我们将掌握从传统的 display 属性到现代的 FlexboxGrid 布局。

与时俱进: 值得一提的是,现代 CSS 自身也在不断进化。例如,现在 CSS 已经原生支持 变量,可以直接解决“硬编码”的问题。我们会在后续章节中学习这些现代特性。


0.3. 环境搭建:一个编辑器,一个浏览器

学习 CSS 的美妙之处在于其 极低的入门门槛。你不需要安装任何复杂的编译器或工具链,只需两个核心软件:

2025

第一步

安装代码编辑器: Visual Studio Code
这是我们编写 HTML 和 CSS 代码的地方。它免费、强大,并拥有丰富的插件生态。

第二步

安装效率插件: Live Server (可选,但强烈推荐)
这个 VS Code 插件能启动一个本地开发服务器,并在你保存文件时自动刷新浏览器,极大地提升开发效率。

  1. 打开 VS Code
  2. 点击扩展图标 (或按 Ctrl+Shift+X)
  3. 搜索并安装由 Ritwick Dey 发布的 Live Server

创建你的第一个项目结构
现在,让我们创建一个用于练习的简单项目文件夹。

1
2
3
4
my-first-css-project/
├── index.html # <-- 我们的 HTML 页面
└── css/
└── style.css # <-- 我们的样式表

0.4. 初试牛刀:链接你的第一张样式表

现在,让我们完成从编写 CSS 到在浏览器中看到效果的完整流程。

1. 编写 HTML 和 CSS 代码

文件路径: my-first-css-project/index.html

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello CSS!</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h1>欢迎来到 CSS 的世界!</h1>
</body>
</html>

文件路径: my-first-css-project/css/style.css

1
2
3
4
5
6
7
8
9
10
11
12
/* 这是一个 CSS 注释 */

/* 选中 body 元素,并设置背景色 */
body {
background-color: #f0f8ff; /* 淡蓝色 */
}

/* 选中 h1 元素,并设置文字颜色和居中 */
h1 {
color: #4682b4; /* 钢蓝色 */
text-align: center;
}

2. 在浏览器中查看结果

  • 如果安装了 Live Server: 右键点击 index.html 文件,选择 Open with Live Server
  • 如果未安装插件: 直接在文件浏览器中右键点击 index.html,选择“在默认浏览器中打开”。

你将会看到一个拥有淡蓝色背景和居中蓝色标题的页面。

工作流已建立: 恭喜!你已经成功建立了最基础的前端开发工作流。从现在开始,你只需修改 css/style.css 文件并保存,然后刷新浏览器(如果使用 Live Server 则会自动刷新),即可看到最新的样式效果。


第一章:CSS 基础核心

摘要: 本章将系统讲解构成 CSS 的三大基石:引入方式继承规则尺寸单位。我们将学习如何将样式代码应用到 HTML 中,理解样式是如何在元素间传递的,并掌握用于精确控制布局和字体的各种单位。本章所有知识点均为后续学习 SCSS 高级特性打下坚实基础。


在本章中,我们将像搭建建筑一样,从最基础的材料和规则开始:

  1. 首先,我们将 认识 CSS,了解它的全称和作为网页“造型师”的核心职责。
  2. 接着,我们将学习 CSS 的三种引入方式,并从工程化的角度确立最佳实践。
  3. 然后,我们将掌握它的一个核心规则——样式的继承性,理解样式的传递机制。
  4. 最后,我们将深入 尺寸单位 的世界,学会如何用代码精确度量页面上的一切。

1.1. CSS 简介:认识层叠样式表

CSS (Cascading Style Sheets) 是“层叠样式表”的缩写。如果说 HTML 是网页的骨架,那么 CSS 就是网页的皮肤和服装。它是一种专门用于定义 HTML 元素视觉表现的语言,负责控制页面的布局、颜色、字体、间距等一切外观相关的样式。

“层叠”(Cascading)的含义: 这个词是 CSS 的核心之一。它意味着样式规则可以从多个来源层层叠加,并且存在一套明确的优先级规则来解决冲突。例如,一个你亲自设置的样式,可以覆盖掉浏览器的默认样式。我们将在后续章节深入学习这套规则。


1.2. CSS 的三种引入方式:行内、内嵌与外链

将 CSS 应用于 HTML 共有三种方式。了解它们的区别并选择正确的方式,是编写可维护代码的第一步。

使用场景
只想给某个唯一元素快速加一条简单样式。

1
2
3
<p style="color: blue; font-size: 20px;">
这段文字将显示为蓝色,20 px。
</p>

不推荐:结构与表现混杂,难以维护。仅作临时调试。

使用场景
样式只服务于当前页面,不对外复用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
<title>内嵌样式示例</title>
<style>
h1 { color: green; text-align: center; }
p { color: grey; }
</style>
</head>
<body>
<h1>绿色居中的标题</h1>
<p>灰色段落。</p>
</body>
</html>

使用场景
多页面共享统一视觉风格,实现完全复用。

css/main.css

1
2
3
4
h1 {
color: purple;
text-align: center;
}

index.html

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>外链样式示例</title>
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<h1>紫色居中的标题</h1>
</body>
</html>

工程化视角:外链样式是现代 Web 的唯一标准。我们写的 .scss 最终会被编译成 .css,再由 <link> 引入,彻底分离结构、表现与行为。

优劣势对比总结

样式表优点缺点使用情况
行内样式书写方便,权重高没有实现结构与样式分离,无法复用极少,仅用于调试
内嵌样式部分分离,仅作用于当前页无法在多页面间复用一般,用于单页应用或特定页面样式
外链样式(推荐) 完全分离,易于维护,可复用需额外维护 .css 文件最多,所有项目的标准实践

1.3. 核心规则:样式的继承性

定义: 继承性是 CSS 的一个基本特性,指的是应用在父元素上的某些 CSS 属性会自动被其内部的子元素所继承,从而无需为每个子元素重复设置相同的样式。

范围: 并非所有属性都可以被继承。

  • 可继承的: 通常是与 文字排版相关的属性,例如:color, font-family, font-size, font-weight, line-height, text-align 等。
  • 不可继承的: 通常是与 元素尺寸、布局和背景相关的属性,例如:width, height, padding, margin, border, background-color 等。

痛点背景: 你希望整个页面的基础字体和颜色保持统一,只需在顶层元素设置一次即可,而不必为每个段落、列表都单独指定。

解决方案: 在 body 标签上设置通用文字样式,利用继承性将其传递给页面内的所有子元素。同时,可以为特定元素(如 h1)设置更具体的样式来覆盖继承值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">
<head>
<title>CSS 继承性示例</title>
<style>
/* 在 body 元素上设置样式 */
body {
color: blue; /* 文本颜色将被子元素继承 */
font-family: Arial, sans-serif; /* 字体将被子元素继承 */
font-size: 16px;
}

/* h1 将覆盖继承来的 color 属性 */
h1 {
color: purple;
/* h1 的 font-family 和 font-size 也会被浏览器默认样式覆盖 */
font-size: 32px;
}

/* p 元素将完全继承 body 的样式 */
p {
/* 无需额外设置,自动继承 */
}
</style>
</head>
<body>
<h1>这是一个标题</h1>
<p>这是一个段落。它继承了body元素的蓝色文本、Arial字体和16px的字号。</p>
<div>
<p>即使在 div 内部,这个段落依然能继承来自 body 的样式。</p>
</div>
</body>
</html>

1.4. 尺寸单位:绝对单位与相对单位

在 CSS 中,单位是用来精确度量长度、尺寸和间距的标尺。它们主要分为两大类。

绝对长度单位

绝对单位的长度是固定的,不会因为其他元素的变化而改变。

  • `px` (像素): Pixel,像素。它是屏幕显示的基本单位,也是 Web 开发中最常用、最基础的绝对单位。对于需要精确控制尺寸的场景非常有用。
  • 其他: cm (厘米), mm (毫米), in (英寸) 等,这些多用于打印样式,在网页开发中极少使用。

相对长度单位 (Relative Length Units)

相对单位的长度不是固定的,它会根据其他元素的尺寸(例如父元素的字体大小或视口宽度)进行计算。这是实现响应式设计的核心。

  • %: 百分比。相对于其父元素的同一属性的值。例如,width: 50% 表示宽度是其父元素宽度的一半。
  • em: 相对于 当前元素 的字体大小 (font-size)。如果当前元素未设置,则相对于 父元素 的字体大小。
  • rem: (重点推荐) Root EM。这是 CSS3 新增的单位,它只相对于 根元素 (<html>) 的字体大小进行计算。这使得它成为构建可伸缩、响应式布局的理想选择,因为我们只需改变根元素的字体大小,就能等比例地缩放整个页面的尺寸。
  • vw: 视口宽度的 1%10vw 等于视口宽度的 10%。
  • vh: 视口高度的 1%10vh 等于视口高度的 10%。

痛点背景: 你需要创建一个元素,它的尺寸能始终根据用户浏览器窗口的大小变化而自适应,例如,一个宽度永远是屏幕宽度一半的盒子。

解决方案: 使用 vwvh 单位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!DOCTYPE html>
<html lang="en">
<head>
<title>相对单位 vw/vh 示例</title>
<style>
body {
margin: 0;
font-family: sans-serif;
text-align: center;
}
.box-vw {
width: 50vw; /* 宽度为视口宽度的50% */
height: 100px;
background-color: #3498db;
color: white;
line-height: 100px;
margin: 20px auto;
}
.box-vh {
height: 30vh; /* 高度为视口高度的30% */
width: 200px;
background-color: #e74c3c;
color: white;
line-height: 30vh;
margin: 20px auto;
}
</style>
</head>
<body>
<div class="box-vw">宽度是 50vw</div>
<div class="box-vh">高度是 30vh</div>
<p>请尝试缩放你的浏览器窗口,观察盒子的变化。</p>
</body>
</html>

1.5. 本章核心速查总结

分类关键项核心描述
核心定义CSS层叠样式表,负责定义 HTML 的视觉表现。
引入方式行内样式 (style="")(不推荐) 破坏结构与表现分离,仅用于调试。
内嵌样式 (<style>)适用于单页面特定样式。
外链样式 (<link>)(推荐) 项目开发标准,实现完全分离和复用。
核心规则继承性子元素会自动继承父元素与 文字排版相关 的属性。
核心单位px(绝对单位) 像素,最基础的单位,用于精确尺寸。
%(相对单位) 相对于父元素。
rem(推荐) 相对于根元素(<html>)字体大小,响应式布局利器。
vw / vh(相对单位) 相对于视口宽/高,适用于全屏或自适应布局。

第二章:精准定位与布局基石

摘要: 本章将全面解析 CSS 选择器的运作机制,从基础的标签选择器到复杂的组合选择器,让您能够像外科医生一样精准地定位到任何 HTML 元素。随后,我们将深入探索 CSS 布局的原子单位——盒模型,理解其内容、内边距、边框和外边距的构成。最后,我们会讲解元素的显示模式,并结合所有知识点,确立样式初始化等工程化最佳实践。


在本章中,我们将分层递进,构建起您对 CSS 布局的完整认知:

  1. 首先,我们将学习 CSS 选择器,这是我们与页面元素进行“对话”的语言。
  2. 接着,我们将探讨 层叠与优先级,理解当多条样式规则作用于同一元素时,浏览器是如何决策的。
  3. 然后,我们将深入研究 盒模型,这是构成所有网页布局的基本单元。
  4. 之后,我们将辨析 元素的显示模式,了解不同元素(如 divspan)在布局中的天然差异。
  5. 最后,我们将学习 样式初始化 的最佳实践,为所有项目打下一个干净、统一的起点。

2.1. CSS 选择器:与元素对话的语言

选择器是 CSS 的核心,它定义了我们的样式规则将应用于哪些 HTML 元素。我们将从最基础的选择器开始,逐步学习如何将它们组合,以实现更精确的定位。

2.1.1. 基础选择器

这是我们工具箱里最基本的四种工具。

1. 标签选择器

  • 用途: 通过 HTML 标签名来匹配文档中所有同名标签。通常用于进行全局的、基础的样式重置。
  • 示例: 统一清除所有 <a> 标签的默认下划线。
    1
    2
    3
    a {
    text-decoration: none;
    }

2. 类选择器

  • 用途: (最常用) 通过标签的 class 属性来匹配元素。它非常灵活,一个类可以被多个元素使用,一个元素也可以拥有多个类。类选择器以 . 开头。
  • 示例: 创建可复用的颜色和尺寸类。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <style>
    .box {
    width: 100px;
    height: 100px;
    border: 1px solid #ccc;
    text-align: center;
    line-height: 100px;
    }
    .bg-red {
    background-color: #e74c3c;
    }
    .bg-green {
    background-color: #2ecc71;
    }
    </style>

    <div class="box bg-red">红色</div>
    <div class="box bg-green">绿色</div>
    <div class="box bg-red">红色</div>

3. ID 选择器)

  • 用途: 通过标签的 id 属性匹配 唯一 的元素。id 在整个 HTML 文档中必须是唯一的。ID 选择器以 # 开头。

  • 示例: 定位页面主导航栏。

    1
    2
    3
    #main-nav {
    background-color: #333;
    }

    注意: 由于 id 的唯一性和高优先级特性,它在 CSS 中应谨慎使用,以避免产生难以覆盖的样式。它的主要应用场景是配合 JavaScript 来操作特定的 DOM 元素。在样式编写中,我们应优先使用类选择器。

4. 通配符选择器

  • 用途: 匹配页面上的 所有 元素。主要用于进行最彻底的样式清除。
  • 示例: 清除所有元素的默认内外边距。
    1
    2
    3
    4
    * {
    margin: 0;
    padding: 0;
    }

    性能警告: 通配符选择器会遍历页面上的每一个元素,对浏览器性能有一定影响。在大型项目中,更推荐使用针对性的 Reset CSS 方案,而非粗暴地使用 *

2.1.2. 组合选择器:更精准的定位

当基础选择器不足以精确描述我们想要选择的元素时,就需要将它们组合起来。

后代选择器 (Descendant Selector - 空格)

  • 含义: 选中 选择器1 内部的 所有 满足 选择器2 的后代元素(儿子、孙子等)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <style>
    /* 只选中 nav 元素内部的 a 标签 */
    nav a {
    color: #e74c3c;
    margin: 0 10px;
    }
    </style>
    <nav>
    <a href="#">首页</a>
    <a href="#">产品</a>
    <div><a href="#">关于我们 (我也是后代)</a></div>
    </nav>
    <a href="#">我是外部链接</a>

子代选择器 (Child Selector - >)

  • 含义: 仅选中 选择器1 内部的 直接 满足 选择器2 的子元素(仅儿子)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <style>
    /* 只选中 nav 元素的直接子元素 a 标签 */
    nav > a {
    color: #3498db;
    margin: 0 10px;
    }
    </style>
    <nav>
    <a href="#">首页 (我是直接子元素)</a>
    <a href="#">产品 (我也是)</a>
    <div><a href="#">关于我们 (我不是)</a></div>
    </nav>

相邻兄弟选择器 (Adjacent Sibling Selector - +)

  • 含义: 选中紧跟在 选择器1 后面的 那一个 满足 选择器2 的兄弟元素。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <style>
    /* 选中 h1 后面紧邻的那个 p 标签 */
    h1 + p {
    color: #9b59b6;
    font-weight: bold;
    }
    </style>
    <h1>这是一个标题</h1>
    <p>我是紧跟在 h1 后面的段落。</p>
    <p>我不是紧邻的段落。</p>

通用兄弟选择器 (General Sibling Selector - ~)

  • 含义: 选中在 选择器1 后面的 所有 满足 选择器2 的兄弟元素。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <style>
    /* 选中 h1 后面所有的兄弟 p 标签 */
    h1 ~ p {
    color: #16a085;
    }
    </style>
    <h1>这是一个标题</h1>
    <div>我不是p标签</div>
    <p>我是 h1 后面的兄弟段落。</p>
    <p>我也是 h1 后面的兄弟段落。</p>

2.1.3. 分组与交集

并集选择器 (Grouping Selector - ,)

  • 含义: 同时选中满足 选择器1 满足 选择器2 的所有元素,为它们应用相同的样式。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <style>
    /* 同时为 h1, h2, p 设置相同的颜色 */
    h1, h2, p {
    color: red;
    }
    </style>
    <h1>主标题</h1>
    <h2>副标题</h2>
    <p>这是一个段落。</p>
    <span>我不会被选中。</span>

交集选择器

  • 含义: 选中 同时 满足 选择器1 选择器2 的那一个元素。选择器之间无分隔符,最常见的用法是 标签.类名
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <style>
    /* 选中既是 p 标签,又拥有 .important 类的元素 */
    p.important {
    color: #c0392b;
    font-size: 18px;
    }
    </style>
    <p class="important">我是重要的段落。</p>
    <div class="important">我不是 p 标签,不会被选中。</div>
    <p>我没有 important 类,不会被选中。</p>

2.2. 层叠与优先级:当规则发生冲突

层叠性: 指的是样式的叠加。多个来源的样式规则(如浏览器默认样式、用户自定义样式、开发者样式)可以共同作用于一个元素。

优先级: 当多个选择器选中同一个元素,并对同一个属性设置了不同的值时,就会发生冲突。此时,浏览器会根据选择器的 优先级 来决定最终应用哪个样式。

优先级的基本规则可以概括为一条链:
!important > 行内样式 > ID 选择器 > 类/属性/伪类选择器 > 标签/伪元素选择器 > 通配符选择器 > 继承的样式

计算规则:

  1. 先比较级别: ID 选择器的优先级永远高于 100 个类选择器。
  2. 级别相同时,比较数量: .nav .link (2 个类) 的优先级高于 .nav (1 个类)。
  3. 权重完全相同时,后来者居上: 在样式表中,写在后面的规则会覆盖前面的规则。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
/* 标签选择器,权重低 */
p {
color: red; /* 被覆盖 */
}
/* 类选择器,权重大于标签选择器 */
.info {
color: green; /* 生效 */
}
/* ID选择器,权重更高 */
#intro {
color: blue; /* 生效,覆盖了.info */
}
</style>
<p id="intro" class="info" style="color: purple;">最终文字颜色?</p>

🤔 思考一下:
在上面的例子中,<p> 标签的最终颜色是什么?

最终颜色是 紫色 (purple)。因为 行内样式 (style="...") 的优先级高于所有选择器(ID, Class, 标签)。如果我们在某个样式后加上 !important,比如 color: orange !important;,那么它将拥有最高优先级。


2.3. 盒模型:网页布局的原子

在 CSS 的世界里,每一个 HTML 元素都可以看作一个矩形的盒子。这个“盒子”由四个部分组成:内容区 (content)内边距 (padding)边框 (border)外边距 (margin)。对网页进行布局,本质上就是排列这些盒子。

img

2.3.1. 内容区

盒子的核心,用于显示文本、图片等实际内容。

  • width: 设置内容区的宽度。
  • height: 设置内容区的高度。
  • overflow: 当内容超出 widthheight 范围时,定义如何处理。常用值有 hidden (隐藏), scroll (显示滚动条), auto (自动)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>盒子内容区示例</title>
<style>
.box {
width: 200px; /* 设置内容区宽度 */
height: 150px; /* 设置内容区高度 */
overflow: auto; /* 内容超出时显示滚动条 */
background-color:aqua;
text-align: center;
line-height: 150px;
}
</style>
</head>
<body>
<div class="box">
这里是盒子的内容,可能会很多超出范围哦。
</div>
</body>
</html>

2.3.2. 边框

包裹在内边距之外的线条。

  • border-width: 边框粗细。
  • border-style: 边框样式,如 solid (实线), dashed (虚线), dotted (点线)。此为必需属性。
  • border-color: 边框颜色。
  • 简写属性: border: 1px solid black;
  • 单边设置: border-top, border-right, border-bottom, border-left
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>边框示例</title>
<style>
.example1 {
width: 300px;
height: 100px;
margin: 10px;
padding: 10px;
border-width: 5px; /* 边框粗细 */
border-style: solid; /* 边框样式:solid(实线), dashed(虚线), dotted(点线), double(双线) */
border-color: blue; /* 边框颜色 */
}

.example2 {
width: 300px;
height: 100px;
margin: 10px;
padding: 10px;
border: 3px dashed red; /* 简写属性:宽度 样式 颜色 */
}

.example3 {
width: 300px;
height: 100px;
margin: 10px;
padding: 10px;
border-top: 4px dotted green; /* 单边设置:border-right, border-bottom, border-left 也可用 */
}
</style>
</head>
<body>
<div class="example1">示例1:分别设置边框属性</div>
<div class="example2">示例2:边框简写属性</div>
<div class="example3">示例3:单边边框设置</div>
</body>
</html>

2.3.3. 内边距

内容区与边框之间的空间,会撑大盒子的可见尺寸

  • 简写属性: padding 可以接受 1 到 4 个值,遵循 上、右、下、左 的顺时针规则。
    • padding: 10px; (四边都是 10px)
    • padding: 10px 20px; (上下 10px, 左右 20px)
    • padding: 10px 20px 30px; (上 10px, 左右 20px, 下 30px)
    • padding: 10px 20px 30px 40px; (上 10px, 右 20px, 下 30px, 左 40px)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>内边距示例</title>
<style>
div {
width: 300px;
height: 200px;
background-color: aqua;
text-align: center;
line-height: 200px;
border: 2px solid #ccc;
padding: 20px;
/* 可选: padding-top, padding-right, padding-bottom, padding-left */
}
</style>
</head>

<body>
<div>这是带内边距的元素</div>
</body>

</html>

2.3.4. 外边距

边框以外的区域,用于控制盒子与盒子之间的距离,是透明的。

  • 简写属性: margin 的赋值规则与 padding 完全相同。
  • 特殊应用: margin: 0 auto; 可以使一个具有固定宽度的块级元素水平居中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>外边距示例</title>
<style>
div {
width: 300px;
height: 200px;
background-color: aqua;
text-align: center;
line-height: 200px;
border: 2px solid #ccc;
margin: 20px;
/* 可选:margin-top, margin-right, margin-bottom, margin-left */
}
</style>
</head>

<body>
<div>这是带外边距的元素</div>
</body>

</html>

2.3.5. 深度解析:外边距合并

这是一个初学者常见的陷阱。在某些情况下,相邻的垂直外边距会发生“合并”(或称“塌陷”),取其中较大的值作为最终的外边距,而不是简单相加。

场景一:相邻兄弟元素

1
2
3
4
5
6
<style>
.box1 { margin-bottom: 100px; background: #009; height:50px;}
.box2 { margin-top: 50px; background: #900; height:50px;}
</style>
<div class="box1"></div>
<div class="box2"></div>

场景二:父子元素(嵌套)
当父元素没有边框或内边距时,子元素的 margin-top 会“塌陷”并作用于父元素。

简单说,就是如果父元素没边框和内边距,子元素设置的 margin - top,看起来就像是给父元素设置的,会让父元素整体下移。比如图中,给子元素 .childmargin - top: 50px,结果父元素 .parent 也跟着下移了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.parent {
width: 200px;
height: 200px;
background-color: #009;
/* 解决方案:触发 BFC */
/* 1. 添加 overflow: hidden; */
/* 2. 添加 border: 1px solid transparent; */
overflow: hidden; /* 推荐 */
}
.child {
width: 50px;
height: 50px;
background-color: #900;
margin-top: 50px; /* 这个 margin 会作用在父元素上 */
}
</style>
<div class="parent">
<div class="child"></div>
</div>

解决方法: 加 overflow: hidden 是因为它能触发父元素形成 BFC(块级格式化上下文),BFC 内部元素布局独立,可阻止外边距合并;加 border(比如 border: 1px solid transparent),是因为它在父子元素间形成了分隔,使子元素的 margin 不再直接作用于父元素,避免了塌陷 。

2.3.6. 现代布局基石:box-sizing

痛点背景: 默认情况下,你给一个盒子设置的 widthheight 只作用于 内容区。当你再添加 paddingborder 时,盒子的实际可见宽度会变大 (width + padding*2 + border*2),这给精确计算布局带来了很大麻烦。

解决方案: 使用 box-sizing 属性改变盒子尺寸的计算方式。

  • content-box (默认值): widthheight 只包含 content。
  • border-box (强烈推荐): widthheight 包含了 content、padding 和 border。这意味着,你设置的 width: 200px; 就是这个盒子最终的可见宽度,即使你再添加 padding 和 border,浏览器会自动向内压缩 content 的空间,而保持总宽度不变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.box {
width: 200px;
height: 100px;
padding: 20px;
border: 10px solid #ccc;
background: #f0f0f0;
margin-bottom: 20px;
}
.content-box {
box-sizing: content-box;
/* 总宽度 = 200 + 20*2 + 10*2 = 260px */
}
.border-box {
box-sizing: border-box;
/* 总宽度 = 200px */
}
</style>
<div class="box content-box">我使用了 content-box</div>
<div class="box border-box">我使用了 border-box (推荐)</div>

最佳实践: 在项目的一开始,就使用通配符选择器为所有元素设置 box-sizing: border-box;,这将极大简化你的布局计算。

1
2
3
4
5
*,
*::before,
*::after {
box-sizing: border-box;
}

2.4. 元素显示模式:块、行内与行内块

HTML 元素根据其默认的显示特性,主要分为三种类型。这是一个非常适合进行强对比的场景。

代表: <div>, <p>, <h1>-<h6>, <ul>, <li>, <form> 等。

特性:

  1. 独占一行: 无论内容多少,总会新起一行。
  2. 可设宽高: 可以自由设置 widthheight
  3. 内外边距: marginpadding 的四个方向都有效。
  4. 默认宽度: 宽度默认为父元素的 100%。
  5. 容器: 通常作为容器,可以包裹任何其他类型的元素(<p> 除外)。

因为 DTD 规定块级元素不能放在 <p> 里,当 <p> 没结束时遇到下一个块元素,浏览器会自动结束当前 <p>,导致无法正常包裹,所以 <p> 一般只包裹行内元素

代表: <span>, <a>, <strong>, <em> 等。

特性:

  1. 同行显示: 可以在一行内与其他行内元素共存。
  2. 宽高无效: 设置 widthheight 无效,其尺寸由内容撑开。
  3. 垂直边距无效: margin-top/bottompadding-top/bottom 无效,不会影响布局。水平方向的内外边距有效。
  4. 包裹内容: 主要用于包裹文字或小图标。

代表: <img>, <input>, <button>。或者任何通过 CSS 设置了 display: inline-block; 的元素。

特性: 它是块元素和行内元素的结合体。

  1. 同行显示 (行内特性): 可以在一行内共存。
  2. 可设宽高 (块特性): 可以自由设置 widthheight
  3. 内外边距有效 (块特性): marginpadding 的四个方向都有效。

2.4.1. 模式转换:display 属性

我们可以使用 display 属性来改变元素天生的显示模式,以满足不同的布局需求。

属性值效果
block将元素转换为块元素。
inline将元素转换为行内元素。
inline-block将元素转换为行内块元素。
none隐藏元素,并且不占据任何空间。
flex(现代布局) 启用 Flexbox 弹性布局。

常见的有:制作导航菜单时,用 display 属性控制子菜单显示隐藏;将行内元素(如)转换为块级元素,方便设置宽高;使用 display: flex 创建弹性布局,适配不同屏幕尺寸(我们后面会详细讲 Flex 布局)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Display 属性演示</title>
<style>
/* 导航菜单样式 */
.navbar {
background-color: #333;
overflow: hidden;
}

.navbar a {
float: left;
display: block;
color: white;
text-align: center;
padding: 14px 20px;
text-decoration: none;
}

.dropdown {
float: left;
overflow: hidden;
}

.dropdown .dropbtn {
font-size: 16px;
border: none;
outline: none;
color: white;
padding: 14px 20px;
background-color: inherit;
font-family: inherit;
margin: 0;
}

.dropdown-content {
display: none; /* 默认隐藏子菜单 */
position: absolute;
background-color: #f9f9f9;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}

.dropdown-content a {
float: none;
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
text-align: left;
}

.dropdown:hover .dropdown-content {
display: block; /* 悬停时显示子菜单 */
}

/* 行内元素转换为块级元素 */
.inline-to-block {
margin: 20px 0;
}

.inline-to-block span {
display: inline-block; /* 转换为行内块级元素 */
width: 100px;
height: 50px;
background-color: #4CAF50;
color: white;
text-align: center;
line-height: 50px;
margin: 5px;
border-radius: 5px;
}


.section {
margin: 30px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}

h2 {
color: #333;
border-bottom: 2px solid #4CAF50;
padding-bottom: 10px;
}
</style>
</head>
<body>
<div class="section">
<h2>1. 导航菜单 - display 控制子菜单显示隐藏</h2>
<nav class="navbar">
<a href="#home">首页</a>
<div class="dropdown">
<button class="dropbtn">产品
</button>
<div class="dropdown-content">
<a href="#">笔记本电脑</a>
<a href="#">台式电脑</a>
<a href="#">平板电脑</a>
</div>
</div>
<div class="dropdown">
<button class="dropbtn">服务
</button>
<div class="dropdown-content">
<a href="#">技术支持</a>
<a href="#">售后服务</a>
<a href="#">培训课程</a>
</div>
</div>
<a href="#contact">联系我们</a>
</nav>
<p>悬停在"产品"或"服务"上查看子菜单效果</p>
</div>

<div class="section">
<h2>2. 行内元素转换为块级元素</h2>
<div class="inline-to-block">
<p>原本的 span 是行内元素,无法设置宽高:</p>
<span>按钮1</span>
<span>按钮2</span>
<span>按钮3</span>
<p>通过 display: inline-block 转换后,可以设置宽高并保持行内特性</p>
</div>
</div>

</body>
</html>

2.5. 最佳实践:样式初始化 (CSS Reset)

痛点背景: 不同浏览器为 HTML 元素设置的默认样式存在微小差异,导致页面在不同浏览器上显示效果不一致。

解决方案: 在编写我们自己的样式之前,首先用一段标准化的代码来清除或统一所有元素的默认样式,这就是 CSS Reset

1
2
3
4
5
6
7
/* case 1: 最省事,但性能差且过于粗暴 */
*{
margin:0;
padding:0;
list-style:none;
border:none;
}

这种方式会重置所有元素,包括那些我们可能不希望重置的(如表单元素),并且使用通配符选择器效率较低。

1
2
3
4
5
6
7
8
9
/* case 2: 相对繁琐,但是是较好选择 */
body, dl, dd, p, h1, h2, h3, h4, h5, h6 {
margin:0;
}
ol, ul {
margin:0;
padding:0;
list-style:none;
}

这种方式更有针对性,只重置了我们关心的块级元素和列表。

在大型项目中,最佳实践是引入一个社区公认的、经过大量测试的 Reset 库。最著名的之一是 Eric Meyer’s “Reset CSS”

只需将这个库的内容复制到你的主样式文件(或 SCSS 主文件)的最顶端,就能确保你的项目在一个干净、统一的样式基线上开始。


2.6. 本章核心速查总结

分类关键项核心描述
基础选择器. (类), # (ID)(推荐) .class 用于样式,#id 用于 JS。
组合选择器A B, A > B后代(空格): 选中内部所有;子代(>): 仅选中直接子元素。
优先级!important > 行内 > ID > 类 > 标签比较时先看级别,再看数量,最后看顺序。
盒模型content, padding, border, margin构成元素布局的四个区域,由内到外。
盒模型计算box-sizing: border-box;(推荐) 使 widthheight 包含 padding 和 border,极大简化布局。
外边距合并margin-top/bottom 合并相邻兄弟或嵌套父子元素的垂直外边距会取最大值,而非相加。
显示模式block独占一行,可设宽高。
inline同行显示,宽高无效,垂直边距无效。
inline-block同行显示,可设宽高。
模式转换display用于在 block, inline, inline-block, none 等模式间切换。

第三章:精准定位:从基础到高级选择器

摘要: 本章将是 CSS 选择器的完全指南。我们将从回顾基础选择器开始,深入学习如何通过组合、属性、状态和结构来构建强大的选择器,实现对任何元素的精准定位。本章的重点是伪类和伪元素,它们是实现交互效果和高级 UI 设计的关键。最后,我们将通过一个实战案例,将所学知识融会贯通。


在本章中,我们将解锁 CSS 的“精确制导”能力:

  1. 首先,我们将快速 回顾基础与组合选择器,巩固已有知识。
  2. 接着,我们将学习 属性选择器,根据元素的“身份证”信息来定位它。
  3. 然后,我们将深入本章的第一个核心——伪类选择器,让页面能够响应用户的交互和状态。
  4. 之后,我们将掌握第二个核心——伪元素选择器,学会在不改变 HTML 的情况下创建和装饰虚拟元素。
  5. 最后,我们将进入 实战环节,综合运用所学知识,构建一个带分隔线的导航菜单效果。

3.1. 基础与组合选择器回顾

上一章我们对核心选择器有了一定认识,现在来快速回顾。

基础选择器

  • 标签选择器:如 p,能选中页面中所有 <p> 标签元素,方便统一设置某类标签样式。
  • 类选择器:像 .container,可选中所有 class 属性值为“container”的元素,在日常样式设置中使用频率高,能对不同标签应用相同样式。
  • ID 选择器#main,用于选中唯一 ID 为“main”的元素,一个页面中 ID 应唯一,常配合 JavaScript 操作特定元素。
  • 通配符选择器*,会选中页面所有元素,不过使用时要谨慎,以免影响性能。

组合选择器

  • 后代选择器nav a,能选中 <nav> 元素内部所有 <a> 元素,不管嵌套多深,方便对特定容器内元素设置样式。
  • 子元素选择器ul > li,仅选中 <ul> 元素的直接子元素 <li>,不会选中更深层级的 <li>,精准控制直接子元素。
  • 相邻兄弟选择器h1 + p,选中紧跟在 <h1> 元素后的第一个 <p> 元素,当需要为特定元素后的紧邻元素设置样式时很有用。
  • 通用兄弟选择器h1 ~ p,选中在 <h1> 元素之后的所有 <p> 元素,只要在源文档顺序上在 <h1> 之后即可,不要求紧邻 。

3.2. 属性选择器:根据标签属性定位

属性选择器允许我们根据 HTML 元素的属性(如 href, target, type)及其值来应用样式,这在处理表单或特定链接时非常有用,如果这里您学过正则表达式,那么理解起来会十分轻松,我们只需要记住 ^ 代表开头,$ 代表全选,* 代表包含即可

写法说明示例
[attr]选中拥有 attr 属性的元素a[title]
[attr=val]选中 attr 属性值 完全等于 val 的元素a[target=_blank]
[attr^=val]选中 attr 属性值以 val 开头 的元素a[href^="https"]
[attr$=val]选中 attr 属性值以 val 结尾 的元素a[href$=".pdf"]
[attr*=val]选中 attr 属性值 包含 val 的元素a[href*="google"]

痛点背景: 你希望为网站上所有指向外部网站的链接(在新窗口打开)和所有指向 PDF 文件的链接自动添加一个特殊的小图标,以提示用户。

解决方案: 我们可以完美地利用属性选择器来实现这个需求,而无需手动为这些链接添加特定的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style>
a {
margin-right: 20px;
color: #3498db;
}
/* 选中所有在新窗口打开的链接 */
a[target="_blank"] {
font-weight: bold;
}
/* 选中所有链接到 https 网站的链接 */
a[href^="https"] {
font-style: italic;
}
/* 选中所有链接到 .pdf 文件的链接 */
a[href$=".pdf"] {
color: green;
}
</style>

<a href="/about">公司简介 (内部链接)</a>
<a href="https://google.com" target="_blank">Google (外部链接)</a>
<a href="files/report.pdf">下载报告 (PDF)</a>

3.3. 伪类选择器:响应用户交互与状态

伪类是 CSS 的一大特色,它允许我们为元素的某些 特殊状态(如鼠标悬停、链接被访问)或 特殊位置(如第一个子元素)应用样式。伪类以单个冒号 : 开头。

3.3.1. 动态与链接伪类

这些伪类与用户的行为和浏览历史相关,是实现 UI 交互效果的基础。

伪类描述
:hover(最常用) 鼠标指针悬停在元素上时。
:active元素被用户激活时(如鼠标按下未松开)。
:focus元素获得焦点时(如通过 Tab 键选中的输入框)。
:link特指 <a> 标签,匹配未被访问过的链接。
:visited特指 <a> 标签,匹配已被访问过的链接。

LVHA 顺序: 在为链接定义样式时,建议遵循 :link -> :visited -> :hover -> :active 的书写顺序。这是一个经典的记忆法则 “LoVe HAte”,可以确保 :hover:active 的效果不会被 :link:visited 覆盖掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.btn {
display: inline-block;
padding: 10px 20px;
background-color: #3498db;
color: white;
text-decoration: none;
border-radius: 5px;
transition: all 0.3s ease; /* 添加一个平滑过渡效果 */
}
/* 鼠标悬停时改变背景色 */
.btn:hover {
background-color: #2980b9;
}
/* 鼠标按下时添加内阴影 */
.btn:active {
box-shadow: inset 0 3px 5px rgba(0,0,0,0.2);
}
</style>

<a href="#" class="btn">一个交互式按钮</a>

3.3.2. 结构性伪类

这类伪类根据元素在 DOM 树中的位置或结构关系来选择元素,非常强大。

伪类描述
:first-child匹配作为其父元素的 第一个 子元素的元素。
:last-child匹配作为其父元素的 最后一个 子元素的元素。
:nth-child(n)匹配作为其父元素的 第 n 个 子元素的元素。
:nth-of-type(n)匹配作为其父元素的 第 n 个同类型 的元素。
:not(selector)匹配不符合 selector 的所有元素。

n 可以是数字、关键字 (odd(奇数), even(偶数)) 或公式 (3n+1)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<style>
/* 匹配第一个div */
div:first-child { color: red; }
/* 匹配最后一个div */
div:last-child { color: blue; }
/* 精确匹配第二个div */
div:nth-child(2) { color: green; }
/* 匹配第一个p */
p:nth-of-type(1) { font-weight: bold; }
/* 匹配不是special类的div */
div:not(.special) { background: yellow; }
</style>
</head>
<body>
<div>第一个div</div>
<div class="special">第二个div</div>
<div>第三个div</div>
<p>段落1</p>
<p>段落2</p>
</body>
</html>
: nth-child vs : nth-of-type
2025-08-25

li:nth-child(2)li:nth-of-type(2) 有什么区别?它们看起来很像。

P
Prorise

这是一个非常关键的区别!

P
Prorise

li:nth-child(2) 的意思是:“找到一个元素,它必须是其父元素的 第 2 个 孩子,并且它自己 恰好 还是一个 <li> 元素”。如果第 2 个孩子是 <div>,那么这个选择器就什么也选不中。

P
Prorise

li:nth-of-type(2) 的意思是:“在其父元素的所有 <li> 孩子中,找到 第 2 个 <li>”。它会忽略其他类型的兄弟元素。

P
Prorise

简单说,nth-child 是“先定位,再看类型”,而 nth-of-type 是“先按类型分组,再定位”。在有混合类型兄弟元素的场景下,nth-of-type 通常更可靠。

示例: 实现一个“斑马条纹”表格,并为第一行和最后一行添加特殊样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<style>
/* 选中所有<ul>下的<li>标签 */
ul li {
/* 去除li标签的默认样式 */
list-style: none;
padding: 10px;
border-bottom: 1px solid #eee;
}

/* 选中第一个<ul>下的<li>标签 */
ul li:first-child {
font-weight: bold;
color: #2980b9;
border-top: 2px solid #2980b9;
}

/* 选中最后一个li标签 */
ul li:last-child {
border-bottom: 2px solid #2980b9;
}

/* 选中所有的奇数行实现斑马条纹 */
ul li:nth-child(odd) {
background-color: #f0f0f0;
}

/* 选中除了有 .highlight 类的所有 li */
ul li:not(.highlight) {
/* 添加过渡效果 */
transition: background-color 0.3s;
}

ul li:hover:not(.highlight) {
background-color: #f0f0f0;
}

.highlight {
background-color: #2980b9;
color: #fff;
}
</style>

<ul>
<li>第一项</li>
<li class="highlight">第二项 (高亮)</li>
<li>第三项</li>
<li>第四项</li>
<li>第五项</li>
</ul>

3.4. 伪元素选择器:创建虚拟元素

伪元素与伪类不同,它不是为已有元素添加特殊状态,而是 创建出一个在 DOM 树中不存在的虚拟元素,并为其添加样式。伪元素在现代 CSS 中以双冒号 :: 开头,以区别于伪类。

伪元素描述
::before在选中元素内容的 前面 插入一个虚拟元素。
::after在选中元素内容的 后面 插入一个虚拟元素。
::first-letter(少用)选中块级元素内容的第一个字母。
::first-line(少用)选中块级元素内容的第一行。
::selection(少用)匹配用户鼠标选中的文本部分。
::placeholder(少用)匹配输入框的占位提示文本。

关键规则: ::before::after 创建的虚拟元素默认是 行内元素。它们必须包含 content 属性(即使是 content: '';)才能生效。

示例: 使用 ::before::after 为引用文本添加装饰性的引号,并自定义选中文本的样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<style>
blockquote {
font-size: 1.2em;
font-style: italic;
color: #555;
background: #f9f9f9;
border-left: 5px solid #ccc;
padding: 10px 20px;
margin: 20px 0;
}
/* 在引用文本前添加一个左引号 */
blockquote::before {
content: "“";
font-size: 2em;
color: #ccc;
line-height: 0.1em;
margin-right: 0.1em;
vertical-align: -0.4em;
}
/* 在引用文本后添加一个右引号 */
blockquote::after {
content: "”";
font-size: 2em;
color: #ccc;
line-height: 0.1em;
margin-left: 0.1em;
vertical-align: -0.45em;
}
/* 自定义鼠标选中文本的样式 */
blockquote::selection {
background-color: #2980b9;
color: white;
}
</style>

<blockquote>CSS 的强大之处在于其看似简单却蕴含无限可能的组合性。</blockquote>

3.5. 实战应用:构建一个带分隔线的导航菜单 (修正版)

目标: 创建一个如下图所示的,干净、稳定、带分隔线的水平导航菜单。

img

动手思路

  1. HTML 结构: 使用 <ul><li> 构建导航的语义化骨架。
  2. 水平布局: 为 <li> 设置 display: inline-block; 实现水平排列,并移除 <ul> 的默认列表样式。
  3. 交互样式: 为 <li> 添加 :hover 伪类以改变背景色,并为 <a> 设置 padding 和基础样式。
  4. 添加分隔线: 为每个 <li> 添加 border-right 来创建分隔线,这比使用伪元素更稳定。
  5. 移除末尾分隔线: 使用 :last-child 伪类,选中最后一个 <li>,并将其 border-right 移除。

Html 结构如下:

1
2
3
4
5
6
7
8
<nav class="main-nav">
<ul>
<li><a href="#">首页</a></li>
<li><a href="#">产品中心</a></li>
<li><a href="#">关于我们</a></li>
<li><a href="#">联系我们</a></li>
</ul>
</nav>

CSS 实现与解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<style>
/* 容器基础样式 */
.main-nav {
text-align: center;
background-color: white;
border-radius: 5px;
}

.main-nav ul {
list-style: none;
padding: 0;
margin: 0;
}

.main-nav li {
/* 让他变为行内块即可并排起来 */
display: inline-block;
font-size: 16px;
/* 在子元素上恢复字体大小 */

/* 核心思路:使用 border-right 作为分隔线,更稳定 */
border-right: 1px solid #ccc;
}

.main-nav a {
display: block;
padding: 15px 20px;
color: #333;
text-decoration: none;
transition: background-color 0.3s;
}

.main-nav li:hover {
background-color: #ddd;
}

.main-nav li:last-child {
border-right: none;
}
</style>

3.6. 本章核心速查总结

分类关键项核心描述
属性选择器[attr^=val], [attr$=val]根据属性值的 开头结尾 进行匹配,常用于链接样式。
动态伪类:hover, :focus, :active实现 UI 交互效果的三剑客。
结构性伪类:nth-child(n)先找第 n 个孩子,再判断类型是否匹配。
p:nth-of-type(n)(推荐) 先按 p 类型分组,再找该类型的第 n 个。
:not(selector)排除特定元素,非常实用。
伪元素::before, ::after(核心) 创建虚拟的行内元素,必须包含 content 属性。
::selection自定义用户用鼠标选中文本时的背景和颜色。
::placeholder自定义输入框占位文本的样式。

第四章:精通文本样式:字体、排版与装饰

摘要: 在之前的章节中,我们已经为了完成布局和交互案例,初步使用了一些基础的文本与字体属性。本章将对这些属性进行一次系统性的、深入的讲解。我们将从基础的 font-familyfont-size 开始,学习如何为网页设定统一且美观的视觉基调;然后,我们会掌握 text-alignline-height 等排版工具,优化用户的阅读体验。最重要的是,本章将引入实战模块,指导您将在第三章学到的高级选择器知识,应用于构建自定义按钮、美化文章列表等常见网页组件。


在本章的学习地图中,我们将:

  1. 首先,深入 字体属性 (font),掌握设定网页视觉基调的工具。
  2. 接着,探索 文本属性 (text),学会精细化地优化段落的阅读体验。
  3. 然后,学习一些 进阶文本控制 技巧,解决文本溢出和垂直对齐等常见问题。
  4. 最后,在 实战环节 中,将所有理论知识融会贯通,亲手打造实用的网页组件。

4.1. 字体属性 (font):奠定视觉基调

字体是网页设计的灵魂。合适的字体属性不仅关乎美观,更直接影响内容的可读性和用户体验。

4.1.1. font-family:指定字体

font-family 属性用于设置文本的字体。由于我们无法保证用户的电脑上安装了我们想要的所有字体,最佳实践是提供一个备选列表,也就是“字体栈”。

痛点背景: 你精心设计了一个使用小众字体的网站,但在用户的电脑上却因为缺少该字体而显示为普通的宋体,完全破坏了设计感。

解决方案: 提供一个优雅降级的字体列表。浏览器会从左到右依次尝试,直到找到第一个可用的字体。列表最后通常会以一个通用的字体族(如 sans-serif)结尾,确保最坏情况下也能显示正确的字体风格。

1
2
3
4
5
6
7
8
9
10
11
.serif-text {
/* 优先使用 Georgia,如果没有,则使用 Times New Roman,
如果还没有,则使用系统默认的任何一种衬线字体 */
font-family: Georgia, "Times New Roman", serif;
}

.sans-serif-text {
/* 优先使用 Helvetica,如果没有,则使用 Arial,
如果还没有,则使用系统默认的任何一种无衬线字体 */
font-family: Helvetica, Arial, sans-serif;
}

当字体名称包含空格时(如 “Times New Roman”),必须用引号将其包裹起来。

4.1.2. font-size:控制大小

font-size 用于设置字体大小。在第一章我们已经接触过单位,这里我们重点对比其实际应用。

  • px: 像素,绝对单位,大小固定,适合需要精确控制的场景。
  • em: 相对单位,相对于 父元素 的字体大小。1.2em 表示父元素字体大小的 1.2 倍。
  • rem: (推荐) 相对单位,只相对于 根元素 (<html>) 的字体大小。这使得它在响应式设计中极易管理,只需调整根字号,整个页面的元素就能等比缩放。

比如在做网页适配不同屏幕大小时,用 rem 就很方便。假设在 html 根元素设置 font-size: 16px,那么页面中 1rem 就相当于 16px2rem 就是 32px。如果要做一个适配手机和平板的网页,当平板屏幕更宽时,把 htmlfont-size 调整为 20px,那么原本设置为 1rem 的元素尺寸就会从 16px 变为 20px,其他以 rem 为单位的元素也会相应等比例变化,这样就能快速实现整体布局和字体大小的响应式调整。


4.1.3. font-weight:控制粗细

font-weight 用于设置字体的粗细。可以使用关键字或数字值。

  • 关键字: normal (默认值,等同于 400),bold (粗体,等同于 700)。
  • 数字值: 从 100 (最细) 到 900 (最粗),以 100 为步长。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Font Weight Demo</title>
</head>
<body>
<p style="font-weight: 100;">Font Weight 100</p>
<p style="font-weight: 200;">Font Weight 200</p>
<p style="font-weight: 300;">Font Weight 300</p>
<p style="font-weight: 400;">Font Weight 400 (normal)</p>
<p style="font-weight: 500;">Font Weight 500</p>
<p style="font-weight: 600;">Font Weight 600</p>
<p style="font-weight: 700;">Font Weight 700 (bold)</p>
<p style="font-weight: 800;">Font Weight 800</p>
<p style="font-weight: 900;">Font Weight 900</p>
</body>
</html>

4.1.4. font-style:控制样式

font-style 主要用于设置文本是否倾斜。

  • normal: 正常显示。
  • italic: 以斜体显示。
1
2
<p style="font-style:normal;">Font Style Normal</p>
<p style="font-style:italic;">Font Style Italic</p>

4.1.5. line-height:控制行高

line-height 定义了每行文本所占据的垂直空间,是排版中至关重要的属性。文字在行高所定义的空间内是 垂直居中 的。

经典技巧:单行文本垂直居中
当一个块级元素的 heightline-height 设置为相同的值时,其中的单行文本就能实现完美的垂直居中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.header-bar {
height: 60px;
background-color: #2c3e50;
color: white;

/* 关键:让行高等于高度 */
line-height: 60px;
}
</style>

<div class="header-bar">
这是一个高度为 60px 的导航栏,文字垂直居中。
</div>

4.1.6. font (简写属性)

为了简化代码,我们可以使用 font 简写属性。顺序非常重要,至少需要指定 font-sizefont-family

这个在开发中会比较少用一点,且阅读性较差,毕竟 font-weight 类似于这样的属性他的语义化更为清晰

语法: font: font-style font-weight font-size/line-height font-family;

1
2
3
4
5
6
7
8
9
10
11
12
13
p {
/* 正常写法 */
font-style: italic;
font-weight: bold;
font-size: 16px;
line-height: 1.5;
font-family: Arial, sans-serif;
}

/* 等价的简写形式 */
p {
font: italic bold 16px/1.5 Arial, sans-serif;
}

4.2. 文本属性 (text):优化阅读体验

这类属性用于控制文本段落的整体外观和布局。

  • color: 设置文字颜色。
  • text-align: 设置块内文本的 水平对齐 方式。可选值有 left, right, center, justify (两端对齐)。
  • text-decoration: 添加或移除文本装饰。none (无装饰) 常用于移除 <a> 标签的默认下划线;underline (下划线);line-through (删除线)。
  • text-indent: 设置块内文本的 首行缩进text-indent: 2em; 是一个经典设置,表示缩进两个当前字体的宽度。
  • letter-spacing / word-spacing: 分别用于微调 字符之间单词之间 的间距。

没指定单位时,line-height 默认是以当前元素 font-size 为基准的倍数,1.6 就表示行高是字体大小的 1.6 倍。这样设置在继承时更灵活,子元素会用自身字体大小乘以这个倍数来计算行高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.article p {
color: #333;
line-height: 1.6;
}
.article p.intro {
text-align: justify;
text-indent: 2em;
letter-spacing: 0.5px;
}
.article a {
text-decoration: none;
color: #3498db;
}
</style>

<div class="article">
<p class="intro">
这是一个段落,用于演示文本样式。它实现了首行缩进两个字符,并且文字两端对齐,字符间距也略有增加,以提升阅读体验。段落中有一个<a href="#">超链接</a>,它的下划线已被移除。
</p>
</div>

4.3. 进阶文本控制

4.3.1. 文本溢出处理

痛点背景: 当一段很长的文本(如文章标题)被放进一个固定宽度的容器时,如果文本不换行,就会溢出容器,破坏布局。

解决方案: 使用“三件套”实现经典的“文本溢出显示省略号 (...)”效果。

  1. white-space: nowrap;: 强制文本在同一行显示,不自动换行。
  2. overflow: hidden;: 隐藏超出容器宽度的部分。
  3. text-overflow: ellipsis;: 将被隐藏的部分用省略号代替。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style>
.title-box {
width: 250px;
border: 1px solid #ccc;
padding: 10px;

/* --- 文本溢出处理三件套 --- */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

<div class="title-box">
这是一个非常非常非常长的标题,它会被截断并显示省略号。
</div>

4.3.2. vertical-align:垂直对齐

这是一个非常容易被误解的属性。

核心要点: vertical-align 只作用于 行内元素 (inline) 和行内块元素 (inline-block)。它控制的是一个元素相对于它所在的 那一行 的基线 (baseline) 的对齐方式,而不是用于块级元素的垂直居中。

痛点背景: 你想让一张小图标和旁边的文字在垂直方向上对齐。

解决方案: 使用 vertical-align: middle;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<style>
.icon {
width: 24px;
height: 24px;
background-color: #3498db;
display: inline-block; /* 确保是行内块元素 */

/* 关键:让图标和文字的中线对齐 */
vertical-align: middle;
}
.label-text {
font-size: 20px;
line-height: 24px; /* 让行高和图标高度一致,效果更佳 */
}
</style>

<div>
<span class="icon"></span>
<span class="label-text">图标与文字对齐</span>
</div>

4.4. 实战:样式化常见元素

现在,我们将综合运用本章以及第三章的知识,完成两个常见的实战任务。

4.4.1. 实战一:从链接到按钮

任务: 将一个普通的 <a> 标签,通过 CSS 样式化为一个具有背景色、圆角、悬停效果的现代化按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style>
.custom-btn {
/* 1. 改变显示模式,使其能设置内外边距 */
display: inline-block;

/* 2. 外观样式 */
background-color: #2ecc71;
color: white;
padding: 12px 24px;
border-radius: 5px;

/* 3. 文本样式 */
font-weight: bold;
text-decoration: none;

/* 4. 添加平滑过渡效果 */
transition: background-color 0.3s;
}

/* 5. 使用 :hover 伪类实现交互 */
.custom-btn:hover {
background-color: #27ae60;
}
</style>

<a href="#" class="custom-btn">注册新账号</a>

4.4.2. 实战二:美化文章列表

任务: 去除 <ul> 的默认项目符号,并为其添加自定义的图标作为新的项目符号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<style>
.feature-list {
list-style: none; /* 1. 去掉默认的小黑点 */
padding-left: 0;
}
.feature-list li {
padding-left: 30px; /* 2. 为伪元素留出空间 */
position: relative; /* 3. 作为伪元素的定位参考 */
line-height: 1.6;
margin-bottom: 10px;
}

/* 4. 使用 ::before 伪元素创建自定义项目符号 */
.feature-list li::before {
content: "✔"; /* 5. 设置内容为一个对勾符号 */
color: #2ecc71;
font-weight: bold;
font-size: 20px;

/* 6. 绝对定位,精确控制位置 */
position: absolute;
left: 0;
top: 0;
}
</style>

<ul class="feature-list">
<li>掌握核心字体属性</li>
<li>优化文本排版布局</li>
<li>实战构建网页组件</li>
</ul>

4.5. 本章核心速查总结

分类关键项核心描述
字体font-family设置字体栈,以通用字体族结尾,如 sans-serif
font-size(推荐) 使用 rem 单位,便于实现整站响应式缩放。
line-height控制行高,可通过 height: value; line-height: value; 实现单行垂直居中。
font简写属性,顺序: style weight size/line-height family
文本text-align控制块内文本 水平 对齐。
text-decorationnone 常用于移除链接下划线。
text-overflowellipsis 配合 overflow:hiddenwhite-space:nowrap 实现溢出省略号。
对齐vertical-align(仅限行内/行内块) 控制元素在 行内 的垂直对齐。

4.6. 疑难解答

学习问答
2025-08-25

Prorise,我有点搞不懂 emrem,它们好像都是相对单位,什么时候该用哪个呢?

P
Prorise

问得好!这是初学者最容易混淆的地方。em 是相对于它的父元素,如果层层嵌套,计算起来就会像套娃一样,很麻烦。

P
Prorise

rem 只跟根元素 <html> 挂钩。所以你只要改一个地方,整个页面的元素大小都能“听话”地一起缩放,做响应式设计时特别方便。所以,记住一句话:做项目,优先用 rem

我看到一个技巧说把 heightline-height 设置成一样的值,文字就垂直居中了,这是什么原理?

P
Prorise

对的,这是个很经典的方法。你可以把 line-height 想象成每一行文字被包裹在一个“隐形”的盒子里,文字本身在这个盒子里面是垂直居中的。当你把这个“隐形盒子”的高度(line-height)撑得和外部容器的 height 一样高时,文字自然也就居中了。不过要记住,这个方法只对单行文本有效。

vertical-align 呢?我试了 vertical-align: middle 想让一个 div 居中,但完全没反应。

P
Prorise

这是另一个常见陷阱!vertical-align 是个“内联世界的规则”,它只对 inlineinline-block 元素起作用,用来调整它们在同一行文字里的对齐位置。而 div 默认是块级元素,自己就占了一整行,没有可以相对齐的“邻居”,所以 vertical-align 对它无效。要让块级元素垂直居中,我们需要动用更强大的布局工具,比如我们后面要学的 Flexbox。


第五章:精通视觉表现:背景、列表与装饰

摘要: 本章我们将专注于提升页面的视觉表现力。您将系统学习如何使用强大的 background 属性为元素添加颜色、渐变、图片等丰富的背景效果,此外,我们还将深入探讨列表、轮廓的自定义样式,以及如何利用 z-index 控制元素的层叠顺序,为您的网页增添更多精致的细节。


在本章的学习地图中,我们将:

  1. 首先,深入探索 背景 (background) 属性全家桶,学习如何为元素打造丰富的视觉层次。
  2. 接着,我们将学习如何通过 列表样式 (list-style) 来自定义项目符号。
  3. 然后,我们将辨析 轮廓 (outline) 与边框的区别,并学习如何控制元素的 层叠顺序 (z-index)
  4. 最后,我们将通过 核心速查总结 来巩固本章所有知识点。

5.1. 背景 (background):网页的“皮肤”

背景是元素内容之下的“画布”,通过一系列 background-* 属性,我们可以精确地控制这个画布的外观。

5.1.1. 核心背景属性

属性说明常用值举例
background-color设置纯色背景#FFF, rgb(0,0,0), transparent
background-image设置背景图片url('path/to/image.jpg')
background-repeat控制图片平铺方式no-repeat (不平铺, 只显示一次), repeat (平铺)
background-position设置图片位置center, top right, 50% 50%
background-size控制图片尺寸cover (铺满), contain (完整显示)
background-attachment背景是否随页面滚动scroll (滚动), fixed (固定)
background (简写)将以上属性写在一行blue url(...) no-repeat center / cover

简写属性 background 顺序提示:

color image repeat attachment position / size (注意 size 前必须有 /)


5.1.2. 核心实战技巧

除了基础属性的设置,background 属性的真正威力体现在其组合应用上。以下是三个在开发中几乎必然会遇到的实战技巧。

技巧一:实现半透明背景 (rgba vs opacity)

  • 场景: 需要一个半透明的叠加层,但又不希望层内的文字也变透明。
  • 方案: 使用 background-color: rgba(...) 而非 opacity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<style>
.demo-container {
display: flex;
/* 使用 flex 布局让盒子并排 */
justify-content: space-around;
padding: 20px;
background-color: #f0f0f0;
}

.box {
width: 45%;
padding: 20px;
color: #333;
border: 2px solid #333;
}

.bad-case {
background-color: red;
/* 错误方式:文字和背景都变透明了 */
opacity: 0.2;
color: yellow;
}

.good-case {
/* 正确方式:仅背景半透明,文字保持不透明 */
background-color: rgba(255, 0, 0, 0.2);
color: yellow;
}
</style>

<div class="demo-container">
<div class="box bad-case">
<h3>错误示范 (opacity)</h3>
<p>这段文字也变得半透明了,难以阅读。</p>
</div>
<div class="box good-case">
<h3>正确示范 (rgba)</h3>
<p>这段文字保持清晰,仅背景是半透明的。</p>
</div>
</div>

技巧二:全屏背景图 (Full-Screen Background)

  • 场景: 网站的首页、登录页或活动页,需要一张高清大图作为背景,铺满整个浏览器视口。
  • 方案: 将背景图应用到 htmlbody 标签,并结合 background-size: coverbackground-attachment: fixed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>全屏背景图示例</title>
<style>
/* 1. 基础重置,确保没有多余的边距 */
html, body {
margin: 0;
padding: 0;
height: 100%;
font-family: sans-serif;
}

html {
/* 2. 设置背景图 */
background-image: url(https://picsum.photos/1920/1080);

/* 3. 让背景图相对于视口固定,不随内容滚动 */
background-attachment: fixed;

/* 4. 确保图片从中心开始显示 */
background-position: center center;

/* 5. 禁止图片平铺 */
background-repeat: no-repeat;

/* 6. 核心:让图片等比缩放,直到完全覆盖背景区域 */
background-size: cover;
}

/* 为了演示滚动效果而添加的内容样式 */
.content {
padding: 50px;
color: white;
text-align: center;
}
.content h1 {
font-size: 4em;
text-shadow: 2px 2px 8px rgba(0,0,0,0.7);
}
.long-content {
height: 1500px; /* 创建一个很高的容器来产生滚动条 */
}
</style>
</head>
<body>
<div class="content">
<h1>沉浸式体验</h1>
<p>向下滚动页面,观察背景图如何保持固定。</p>
</div>
<div class="long-content"></div>
</body>
</html>

技巧三:用背景图优雅填充盒子)

  • 场景: 卡片列表、相册或用户头像展示。容器尺寸是固定的,但内部的图片高宽比各不相同,需要让图片填满容器且不被拉伸变形。
  • 方案: 不要使用 <img> 标签,而是用一个 <div> 作为容器,并将图片设为其 background-image,再利用 background-size 控制填充方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<style>
.card-container {
display: flex;
justify-content: center;
gap: 40px;
margin: 20px 0;
}

.card {
width: 200px;
height: 250px;
border: 1px solid #ccc;
border-radius: 8px;
background-repeat: no-repeat;
background-position: center;
}

#card-cover {
background-size: cover;
background-image: url(https://picsum.photos/400/300?random=1);
}

#card-contain {
background-size: contain;
background-color: #f0f0f0;
background-image: url(https://picsum.photos/400/300?random=2);
}

.description {
text-align: center;
margin-top: 10px;
font-size: 14px;
color: #555;
}
</style>


<div class="card-container">
<div class="card" id="card-cover"></div>
<div class="card" id="card-contain"></div>
</div>

<p class="description">左:cover (填满容器) | 右:contain (完整显示)</p>

5.2. 列表样式 (list-style)

在第四章的实战中我们学习了使用伪元素 ::before 来创建更灵活的自定义列表符号,对于 list-style 我们最常用的仅仅只是将它设置为 none 即无符号

  • list-style-type: 改变项目符号的类型,例如 square (方块), circle (空心圆), upper-roman (大写罗马数字), none (无符号)。
  • list-style-position: outside (默认,符号在列表项外) 或 inside (符号在列表项内,会随文本缩进)。
  • list-style-image: 使用 url() 指定一张图片作为项目符号。
  • list-style (简写属性): list-style: type position image;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.list-a { list-style: square inside; }
.list-b { list-style-type: upper-roman; }
</style>

<ul class="list-a">
<li>第一项 (方块, inside)</li>
<li>第二项</li>
</ul>

<ol class="list-b">
<li>第一项 (大写罗马数字)</li>
<li>第二项</li>
</ol>

5.3. 轮廓 (outline) 与层叠 (z-index)

5.3.1. 轮廓 (outline):不占空间的“高亮框”

outline 是绘制于元素 border 之外的一条线,它与 border 最大的区别在于:

核心区别: outline 不属于 盒模型的一部分,它不占据任何布局空间,不会把其他元素推开。而 border 会增加元素的实际尺寸,影响布局。

outline 常用于调试或在元素获得焦点时(:focus)提供视觉反馈,在开发中我们也并不是很常用,简单了解即可

  • outline-style: solid, dashed, dotted 等。
  • outline-width: 轮廓宽度。
  • outline-color: 轮廓颜色。
  • outline-offset: 轮廓与边框之间的距离。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.box { width: 100px; height: 50px; margin: 20px; float: left; }
.has-border {
border: 10px solid #e74c3c;
background-color: #fbe9e7;
}
.has-outline {
outline: 10px solid #3498db;
background-color: #eaf4fb;
}
</style>

<div class="box has-border">我有 Border</div>
<div class="box has-outline">我有 Outline</div>

5.3.2. z-index:控制元素的堆叠顺序

网页是三维的,除了 X 轴和 Y 轴,还存在一个垂直于屏幕的 Z 轴。z-index 属性就用于控制元素在 Z 轴上的堆叠顺序。

前置条件: z-index 属性 仅在 元素定义了 position 属性(且值不为 static)时才有效。我们将在下一章详细学习 position,这里先作初步了解。

  • z-index 的值是一个整数,可以是正数、负数或 0。
  • 值越大的元素,层级越高,会显示在值小的元素的上方。
  • 如果没有设置 z-index,则在 HTML 中后出现的元素会叠在先出现的元素之上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style>
.overlap-box {
width: 150px;
height: 150px;
position: absolute; /* 必须设置定位才能使 z-index 生效 */
font-size: 24px;
color: white;
text-align: center;
line-height: 150px;
}
.red-box {
background-color: #c0392b;
top: 20px;
left: 20px;
z-index: 2; /* z-index 值较大,显示在上面 */
}
.blue-box {
background-color: #2980b9;
top: 70px;
left: 70px;
z-index: 1; /* z-index 值较小,显示在下面 */
}
</style>

<div class="overlap-box red-box">Z-Index: 2</div>
<div class="overlap-box blue-box">Z-Index: 1</div>

5.4. 本章核心速查总结

分类关键项核心描述
背景background-color使用 rgba() 实现仅背景半透明,是最佳实践。
background-sizecover (裁剪铺满) 和 contain (完整显示) 是响应式背景的关键。
background-position配合精灵图(Sprites)使用负值来定位图标,是性能优化技巧。
background简写属性,顺序: color image repeat attachment position / size
列表list-style-type设置项目符号样式,可用 none 去除。
list-style-positioninside (内) 或 outside (外)。
轮廓outline不占据布局空间,不影响周围元素,常用于 :focus 状态。
层叠z-index必须配合 position (非 static) 使用。数值越大,层级越高。

第六章:CSS 布局:从 Flexbox 到 Grid 的布局之道

摘要: 欢迎来到 CSS 世界的“终局之战”——布局。可以毫不夸张地说,掌握了布局,就等于掌握了 99% 的 CSS。本章是一个内容极其丰富的“大章节”,我们将系统性地学习从一维的 Flexbox 到二维的 Grid 这两大现代布局利器,并掌握 Position 定位的精准微操与 Multi-column 的巧妙应用。更重要的是,我们会将所有这些技术融合进“响应式设计”的思维框架中,让您有能力构建适应任何屏幕尺寸的现代化网页。请务必多加练习,本章的知识将是您未来前端生涯中最宝贵的财富。

学习路径说明: 您会注意到,本章我们将重点讲解 Flexbox 和 Grid,而对 float (浮动) 等传统技术则一笔带过。这是因为,float 最初是为“图文环绕”而设计的,用它来做全局布局是一种“滥用”,会带来层叠塌陷、需要清除浮动等无穷的副作用,在 2025 年的今天,它已被现代布局方案完全取代。我们的目标是直接学习当前业界的最佳实践,而非背负历史包袱。


在本章的学习地图中,我们将一步步成为布局大师:

  1. 首先,我们将掌握 一维布局的王者——Flexbox,完美解决组件内的对齐问题。
  2. 接着,我们将学习 二维布局的终极方案——Grid,随心所欲地绘制整个页面蓝图。
  3. 然后,我们会探索 多列布局 (Multi-column) 的妙用,用最简单的代码实现瀑布流。
  4. 之后,我们将学习 Position 定位,掌握脱离常规文档流的“魔法”。
  5. 最后,我们将所有知识融会贯通,学习 响应式设计的核心,让我们的页面在手机、平板和桌面端都能完美呈现。

6.1. Flexbox 弹性布局:一维空间的统治者

Flexbox 是为 一维布局 而生的。无论是水平行内还是垂直列内,只要你需要在一条轴线上对齐、分布、排序一组元素,Flexbox 就是最简单、最强大的工具。

6.1.1. 核心概念:主轴与交叉轴

一旦为一个元素设置了 display: flex;,它就成为了一个 Flex 容器 (flex container),其所有直接子元素都自动成为 Flex 项目 (flex item)

容器内部存在两根无形的轴:

  • 主轴 (main axis): 所有项目默认沿着主轴排列。默认是水平方向。
  • 交叉轴 (cross axis): 与主轴垂直的轴。默认是垂直方向。

6.1.2. 容器属性 (Flex Container)

这些属性写在 父元素 上,用于指挥所有子元素的整体排列。我们将逐一深入讲解。

1. flex-direction:定义主轴方向

此属性决定了项目排列的 方向,即主轴是水平还是垂直。

描述
row默认值。主轴为水平方向,起点在左端。
row-reverse主轴为水平方向,起点在右端。
column主轴为垂直方向,起点在上沿。
column-reverse主轴为垂直方向,起点在下沿。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    <style>
.direction-demo {
display: flex;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.direction-demo div {
width: 60px;
height: 60px;
background: #add8e6;
text-align: center;
line-height: 60px;
margin: 5px;
}
</style>

<strong>flex-direction: row (默认)</strong>
<div class="direction-demo" style="flex-direction: row;">
<div>1</div><div>2</div><div>3</div>
</div>
<strong>flex-direction: row-reverse</strong>
<div class="direction-demo" style="flex-direction: row-reverse;">
<div>1</div><div>2</div><div>3</div>
</div>
<strong>flex-direction: column</strong>
<div class="direction-demo" style="flex-direction: column;">
<div>1</div><div>2</div><div>3</div>
</div>
<strong>flex-direction: column-reverse</strong>
<div class="direction-demo" style="flex-direction: column-reverse;">
<div>1</div><div>2</div><div>3</div>
</div>

2. flex-wrap:定义是否换行

此属性决定了当一条轴线排不下所有项目时,项目是否换行。

描述
nowrap默认值。不换行,所有项目会被压缩以适应单行。
wrap换行,第一个项目在新行的上方。
wrap-reverse换行,第一个项目在新行的下方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    <style>
.wrap-demo { display: flex; width: 220px; border: 1px solid #ccc; margin-bottom: 10px; }
.wrap-demo div { width: 80px; height: 80px; background: #b0e0e6; margin: 5px; text-align: center; line-height: 80px; }
</style>
<strong>flex-wrap: nowrap (默认, 项目被压缩)</strong>
<div class="wrap-demo">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<strong>flex-wrap: wrap</strong>
<div class="wrap-demo" style="flex-wrap: wrap;">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<strong>flex-wrap: wrap-reverse</strong>
<div class="wrap-demo" style="flex-wrap: wrap-reverse;">
<div>1</div>
<div>2</div>
<div>3</div>
</div>

3. justify-content:定义主轴对齐方式

这是最常用的容器属性之一,它定义了项目在 主轴 上的对齐和分布方式。

描述
flex-start默认值。项目向主轴起点对齐。
flex-end项目向主轴终点对齐。
center项目在主轴上居中。
space-between两端对齐,项目之间的间隔都相等。
space-around每个项目两侧的间隔相等。项目之间的间隔比项目与边框的间隔大一倍。
space-evenly所有间隔完全相等,包括项目与边框之间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    <style>
.jc-demo { display: flex; height: 80px; border: 1px solid #ccc; margin-bottom: 10px; }
.jc-demo div { width: 60px; background: #87ceeb; text-align: center; line-height: 80px; }
</style>

<strong>flex-start (默认):主轴向前对齐</strong>
<div class="jc-demo" style="justify-content: flex-start;"><div>1</div><div>2</div><div>3</div></div>
<strong>flex-end:主轴向后对齐</strong>
<div class="jc-demo" style="justify-content: flex-end;"><div>1</div><div>2</div><div>3</div></div>
<strong>center:主轴居中</strong>
<div class="jc-demo" style="justify-content: center;"><div>1</div><div>2</div><div>3</div></div>
<strong>space-between:主轴两端对齐</strong>
<div class="jc-demo" style="justify-content: space-between;"><div>1</div><div>2</div><div>3</div></div>
<strong>space-around:主轴两端对齐,项目间距相等</strong>
<div class="jc-demo" style="justify-content: space-around;"><div>1</div><div>2</div><div>3</div></div>
<strong>space-evenly:主轴两端对齐,项目间距相等</strong>
<div class="jc-demo" style="justify-content: space-evenly;"><div>1</div><div>2</div><div>3</div></div>

4. align-items:定义交叉轴对齐方式 (单行)

它定义了项目在 交叉轴(默认是垂直方向)上的对齐方式。

描述
stretch默认值。如果项目未设置高度或设为 auto,将占满整个容器的高度。
flex-start交叉轴的起点对齐。
flex-end交叉轴的终点对齐。
center交叉轴的中点对齐。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.ai-demo { display: flex; height: 500px; border: 1px solid #ccc; margin-bottom: 10px; }
.ai-demo div { width: 60px; background: #b0e0e6; text-align: center; line-height: 150px; }
</style>
</head>

<body>
<!-- 可以在这里调整属性去观察他们的区别 -->
<div class="ai-demo" style="align-items: flex-end;">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>

</html>

5. align-content:定义多行内容的对齐方式

注意: 此属性仅在项目 换行 后(即 flex-wrap: wrap; 且有多行内容时)才生效。它控制的是 多根轴线 作为一个整体,在交叉轴上的对齐方式。

描述
stretch默认值。轴线占满整个交叉轴。
flex-start与交叉轴的起点对齐。
flex-end与交叉轴的终点对齐。
center与交叉轴的中点对齐。
space-between与交叉轴两端对齐,轴线之间的间隔平均分布。
space-around每根轴线两侧的间隔都相等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style>
.ac-demo {
display: flex;
flex-wrap: wrap; /* 必须换行才能生效 */
height: 300px;
width: 250px;
border: 1px solid #ccc;
margin-bottom: 10px;
}
.ac-demo div {
width: 60px;
height: 60px;
background: #f0f8ff;
margin: 5px;
text-align:center;
line-height:60px;
}
</style>
<strong>align-content: center (可修改查看其它效果)</strong>
<!-- 尝试修改align-content的值去观察他们的区别 -->
<div class="ac-demo" style="align-content: flex-end;">
<div>1</div><div>2</div><div>3</div>
<div>4</div><div>5</div><div>6</div>
<div>7</div><div>8</div><div>9</div>
</div>

6. flex-flow:方向与换行的简写

flex-flowflex-directionflex-wrap 两个属性的简写形式,用的很少,知道就可以了

1
2
3
4
.container {
/* 等同于 flex-direction: row; flex-wrap: wrap; */
flex-flow: row wrap;
}

6.1.3. 项目属性 (Flex Item)

这些属性写在 子元素(项目) 上,用于定义单个项目的特殊行为。

属性核心描述
order改变项目排列顺序 (数值越小越靠前)
flex-grow定义项目的放大比例 (分配剩余空间)
flex-shrink定义项目的缩小比例 (处理空间不足)
flex-basis定义项目的基础尺寸
flex(核心) 上面三者的简写,如 flex: 1 1 0,代表着
align-self允许单个项目覆盖容器的 align-items
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.flex-container-item { display: flex; align-items: flex-start; height: 150px; border: 1px solid #ccc; }
.item-prop { padding: 10px; border: 1px solid #999; }
.item-p1 { background-color: #fbe9e7; order: 2; /* 排在后面 */ }
.item-p2 { background-color: #e3f2fd; flex-grow: 1; /* 占据所有剩余空间 */ }
.item-p3 { background-color: #e8f5e9; align-self: center; /* 自己居中对齐 */ }
.item-p4 { background-color: #f9fbe7; order: -1; /* 排在最前面 */ }
</style>
<div class="flex-container-item">
<div class="item-prop item-p1">Item 1 (order: 2)</div>
<div class="item-prop item-p2">Item 2 (flex-grow: 1)</div>
<div class="item-prop item-p3">Item 3 (align-self: center)</div>
<div class="item-prop item-p4">Item 4 (order: -1)</div>
</div>

6.1.4. 实战:通过游戏学习布局

任务: 通关 24 关 flexboxfroggy,通过我们学习的知识

1
2
3
4
flex-wrap:wrap-reverse;
align-content:space-between;
flex-direction:column-reverse;
justify-content:center;

6.2. Grid 网格布局:二维平面的上帝视角

如果说 Flexbox 是一维的专家,那么 Grid 就是二维布局的上帝。它将容器划分为行和列,允许我们将项目精确地放置在任意单元格中,是构建整个页面宏观布局的终极方案。

6.2.1. 核心概念

image-20250825214315628

  • Grid 容器 (grid container): 应用 display: grid 的元素。
  • Grid 项目 (grid item): 容器的直接子元素``。
  • Grid 线 (grid line): 划分网格的水平和垂直线。
  • Grid 轨道 (grid track): 两条相邻网格线之间的空间(即一行或一列)。
  • Grid 单元格 (grid cell): 四条网格线包围的最小单位。
  • Grid 区域 (grid area): 一个或多个单元格组成的矩形区域。

6.2.2. 容器属性

这些属性写在父元素上,用于定义整个网格的结构和对齐方式。

1.定义网格轨道

  • grid-template-columns
  • grid-template-rows

这两个核心属性分别用于定义网格的列和行。

描述
<length>使用 px, em 等固定单位。
<percentage>使用相对于容器尺寸的百分比。
fr(核心) "分数"单位,用于按比例分配可用空间。
repeat()用于创建重复的轨道模式,如 repeat(3, 1fr) 表示创建3个等宽的列。
minmax()定义一个尺寸范围,如 minmax(100px, 1fr) 表示最小100px,最大占一份。
auto由浏览器根据内容决定尺寸。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.grid-template-demo {
display: grid;
border: 1px solid #ccc;
/* 定义三列:第一列 100px,第二列 auto,第三列占剩余空间的 1 份 */
grid-template-columns: 100px auto 1fr;
/* 定义两行:都占剩余空间的 1 份 */
grid-template-rows: repeat(2, 1fr);
gap: 10px;
}

.grid-template-demo div {
background: #f0f8ff;
border: 1px solid #333;
text-align: center;
padding: 20px 0;
}
</style>
</head>

<body>
<div class="grid-template-demo">
<div>Row 1, Col 1<br>100px</div>
<div>Row 1, Col 2<br>auto</div>
<div>Row 1, Col 3<br>1fr</div>
<div>Row 2, Col 1<br>100px</div>
<div>Row 2, Col 2<br>auto</div>
<div>Row 2, Col 3<br>1fr</div>
</div>
</body>

</html>

2.定义网格区域

  • grid-template-areas

这是一个极具表现力的属性,允许你通过“画出”区域名称来定义布局。

描述
字符串每个字符串代表一行,字符串内的名称定义了该行的列区域。
.一个点号 (.) 代表一个空的单元格。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.areas-demo {
display: grid;
height: 300px;
/* 定义四列 */
grid-template-columns: repeat(4, 1fr);
/* 定义三行 其中第一行占100px 第二行占auto 第三行占100px */
grid-template-rows: 100px auto 100px;
gap: 10px;
/* "画出"布局 */
grid-template-areas:
"header header header header"
"sidebar main main ."
"footer footer footer footer";
}
.areas-demo div { display: grid; place-items: center; }
.area-header { grid-area: header; background-color: #add8e6; }
.area-sidebar { grid-area: sidebar; background-color: #b0e0e6; }
.area-main { grid-area: main; background-color: #e0ffff; }
.area-footer { grid-area: footer; background-color: #add8e6; }
</style>
</head>

<body>
<div class="areas-demo">
<div class="area-header">Header</div>
<div class="area-sidebar">Sidebar</div>
<div class="area-main">Main</div>
<div class="area-footer">Footer</div>
</div>
</body>

</html>

grid-area 是一个功能非常强大的属性,它既可以用于将项目放置到命名区域,也可以作为基于网格线定位的终极简写形式。

方式一:分配到命名区域
这是 grid-areagrid-template-areas 配合使用的场景,用法非常直观,也如上述示例一样

方式二:作为网格线定位的简写
grid-area 也是 grid-row-start / grid-column-start / grid-row-end / grid-column-end 四个属性的简写。

语法: grid-area: <row-start> / <col-start> / <row-end> / <col-end>;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.lines-shorthand-demo {
display: grid;
background: #f0f0f0;
border: 3px dashed #ccc;
height: 300px;
/* 创建一个五行五列的网格 */
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(5, 1fr);
gap: 5px;
}

.lines-shorthand-demo div {
display: grid;
place-items: center;
background-color: #ffcdd2;
}

.highlight-item {
/* grid-row-start: 2;
grid-column-start: 2;
grid-row-end: 5;
grid-column-end: 5;
的简写形式
*/
/* 表示我期望他从第二行开始第二列开始 */
/* 第五行第五列结束,要注意,这里的五列其实是不包含的,如果要真实到五列需要吧值改为6 */
grid-area: 2 / 2 / 5 / 5;
background-color: #c8e6c9;
color: #1b5e20;
font-weight: bold;
}
</style>
</head>

<body>
<!-- 这也是类似于百度新闻那种类似的布局,可以采用如下的方法快速创建你需要的区域 -->
<div class="lines-shorthand-demo">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div class="highlight-item">Highlight Area</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>

</html>

3. gap:定义网格间距

用于设置行与行、列与列之间的间距。

描述
gap: <row-gap> <column-gap>;分别设置行间距和列间距。
gap: <value>;行间距和列间距都设为同一个值。

4. 单元格内容对齐

这两个属性用于控制项目内容在其所在的单元格内部的对齐方式。可以把单元格想象成一个小房间,而项目内容(文字、图片等)就是房间里的家具,*-items 决定了家具如何在这个房间里摆放。

属性核心描述
justify-items单元格内容的水平对齐
align-items单元格内容的垂直对齐
  • start: 对齐到单元格的起始边(左/上)。
  • end: 对齐到单元格的结束边(右/下)。
  • center: 在单元格内居中。
  • stretch: (默认值) 将项目内容拉伸,填满整个单元格空间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.items-align-demo {
display: grid;
height: 400px;
width: 400px;
border: 3px dashed #ccc;
background: #f0f0f0;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 10px;
border: 1px solid #ccc;
}

.items-align-demo div {
display: grid;
background-color: #e0e0e0;
padding: 10px;

/* 尝试修改这两个值来观察所有单元格内容的变化 */
justify-items: center;
/* 水平居中 */
align-items: center;
/* 垂直底部对齐 */
/* 他有一个简写属性,如下: */
/* place-items: center; */
}
</style>
</head>

<body>
<div class="items-align-demo">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
</body>

</html>

5. 网格整体对齐

核心前提:这两个属性只有在网格的总尺寸小于其容器尺寸时才会生效。它们控制的是整个网格(所有单元格作为一个整体),在容器这个“大画框”里的对齐和分布方式。

属性核心描述
justify-content整个网格作为一个整体的水平对齐与分布
align-content整个网格作为一个整体的垂直对齐与分布
  • start: 整体网格靠向容器的起始边(左/上)。
  • end: 整体网格靠向容器的结束边(右/下)。
  • center: 整体网格在容器内居中。
  • space-between / space-around / space-evenly: 控制网格轨道之间的分布方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.content-align-demo {
display: grid;
/* 容器尺寸大于网格总尺寸,创造出可用空间 */
height: 400px;
width: 400px;
background: #f0f0f0;
border: 3px dashed #ccc;

/* 网格的轨道总尺寸需要小于容器 */
/* 这里相当于设置网格的轨道总尺寸为200px */
grid-template-columns: 100px 100px;
grid-template-rows: 100px 100px;

gap: 10px;
/* 尝试修改这两个值来观察整个网格的变化 */
justify-content: start;
align-content: start;
}

.content-align-demo div {
background-color: #c8e6c9;
display: grid;
/* 让内容在网格中居中 */
/* 这是一个简写属性,相当于设置justify-items和align-items */
place-items: center;
}
</style>
</head>

<body>
<div class="content-align-demo">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>

</html>

6.2.3. 项目属性 (Grid Item)

这些属性写在**子元素(项目)**上,用于定义单个项目在网格中的位置和行为。

1. 基于网格线定位

通过指定项目从哪条网格线开始、到哪条网格线结束,来放置项目。

属性描述
grid-column-start / end定义项目占据的列范围。
grid-row-start / end定义项目占据的行范围。
grid-column / grid-row上述属性的简写,如 1 / 31 / span 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.lines-demo {
display: grid;
background: #f0f0f0;
border: 3px dashed #ccc;
/* 创建一个4列2行的网格 */
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(2, 100px);
gap: 10px;
}

.lines-demo div {
display: grid;
place-items: center;
}

.item-a {
/* 从第2条列线开始,跨越2列 */
grid-column: 2 / span 2;
/* 从第1条行线开始,到第3条行线结束 */
grid-row: 1 / 3;
background-color: #ffcdd2;
}

.item-b {
background-color: #c8e6c9;
}

.item-c {
background-color: #bbdefb;
}

.item-d {
/* 例如我想让item-d 去到第四列的第二行,那么我需要这样写: */
grid-column: 4;
grid-row: 2;
background-color: #ffcdd2;
}
</style>
</head>

<body>
<div class="lines-demo">
<div class="item-a">A</div>
<div class="item-b">B</div>
<div class="item-c">C</div>
<div class="item-d">D</div>
</div>
</body>

</html>

2. justify-selfalign-self

允许单个项目覆盖容器定义的 justify-itemsalign-items,实现独立对齐。

属性描述
justify-self单个项目内容的水平对齐
align-self单个项目内容的垂直对齐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.self-demo {
display: grid;
height: 400px;
width: 400px;
border: 3px dashed #ccc;
background: #f0f0f0;
/* 创建一个3列1行的网格 */
grid-template-columns: repeat(3, 1fr);
grid-template-rows: 100px;
gap: 10px;
/* 容器统一垂直居中 */
}

.self-demo div {
background-color: #f3e5f5;
padding: 10px;
}

.special-item {
align-self: center;
/* 我自己要底部对齐 */
}
</style>
</head>

<body>
<div class="self-demo">
<div>Item 1</div>
<div class="special-item">Special Item</div>
<div>Item 3</div>
</div>
</body>

</html>

6.2.4. 实战:通过游戏学习布局

任务: 通关 28 关 Grid Garden,通过我们学习的知识,将萝卜种到它们该去的地方。


6.3. Position 定位:脱离常规的精准微操

摘要: 本节我们将学习一种全新的布局维度——定位 (Positioning)。您将学习如何使用 position 属性,让元素“脱离”常规的文档流,实现覆盖、固定、粘滞等高级效果。这是实现模态框、下拉菜单、固定导航栏等复杂 UI 组件的必备知识。

6.3.1. 核心前提:理解文档流

在学习定位之前,我们必须先清晰地理解什么是“常规文档流”。在默认状态下,页面元素会遵循一套既定的排列规则:

  • 块级元素 (Block): 从上到下垂直排列,每个元素独占一行。
  • 行内元素 (Inline): 在水平方向上从左到右依次排列,遇到行尾会自动换行。

这套规则就是文档流。而 position 属性,就是赋予我们打破这套常规规则,让元素“飞”出常规队列的能力。

1
2
3
4
5
6
7
8
9
10
11
12
<style>
.flow-box { border: 1px solid #ccc; padding: 10px; margin: 10px; }
.flow-box div { background-color: #e3f2fd; border: 1px solid #90caf9; padding: 10px; margin-bottom: 5px; }
.flow-box span { background-color: #fbe9e7; border: 1px solid #ffab91; padding: 5px; }
</style>
<div class="flow-box">
<div>块级元素 1</div>
<div>块级元素 2</div>
<span>行内元素 A</span>
<span>行内元素 B</span>
<span>行内元素 C</span>
</div>

6.3.2. 定位五模式核心对比

position 属性共有五个值,下表可以帮助我们从宏观上快速把握它们的核心区别。

模式脱离文档流定位参照物核心应用场景
static默认状态,无定位
relative否 (仍占原始空间)自身原始位置作为 absolute 的参照物
absolute最近的非 static 祖先弹窗、角标、覆盖层
fixed浏览器视口固定导航、返回顶部
sticky滚动到阈值后最近的滚动父级吸顶导航、表格标题

6.3.3. 核心模式详解与实战

相对定位 (relative)

相对定位非常特殊,它遵循“身在曹营心在汉”的原则:

  1. 它在文档流中依然占据着原来的空间,不会影响其他元素的布局。
  2. 它可以使用 top, right, bottom, left 属性,相对于自己的原始位置进行偏移。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style>
.relative-demo div { width: 100px; height: 100px; display: grid; place-items: center; color: white; }
.box-a { background-color: #a5d6a7; }
.box-b {
background-color: #ef9a9a;
position: relative; /* 启用相对定位 */
top: 30px; /* 向下偏移 30px */
left: 30px; /* 向右偏移 30px */
}
.box-c { background-color: #90caf9; }
</style>
<div class="relative-demo">
<div class="box-a">Box A</div>
<div class="box-b">Box B (Relative)</div>
<div class="box-c">Box C</div>
</div>

绝对定位 (absolute) 与 “父相子绝”

这是定位中最核心、最重要的应用模式。

  • position: absolute; 会让元素完全脱离文档流
  • 它会向上寻找所有祖先元素,以第一个 position 值不为 static 的祖先元素作为自己的定位参照物。
  • 如果找不到这样的祖先,它最终会相对于 <body> 定位。

为了将一个绝对定位元素限制在其直接父元素内部,我们诞生了黄金组合——“父相子绝”:

  • 父级: position: relative; (让父级成为定位参照物,但自身不脱离文档流)
  • 子级: position: absolute; (让子级脱离文档流,并相对于父级定位)

实战: 在卡片右上角添加“Hot”徽章。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        <style>
.card {
width: 250px; border: 1px solid #ccc; border-radius: 8px;
font-family: sans-serif; margin: 50px auto;
/* 1. 父元素设为相对定位,成为定位参照物 */
position: relative;
}

.card img {
width: 100%;
/* 默认img是inline-block级别,但我们不希望他可以在一行内共存
而是融入到我们的父元素card容器中以单独行做排列 */
display: block; border-radius: 8px 8px 0 0;
}

.card-body {
padding: 15px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.badge {
/* 2. 子元素设为绝对定位,脱离文档流 */
position: absolute;
/* 3. 相对于 .card 的边缘进行定位 */
top: -10px; right: -10px; background-color: #e74c3c;
color: white; padding: 5px 10px; border-radius: 5px;
font-size: 14px;
}
</style>
<div class="card">
<img src="https://picsum.photos/seed/phone/400/250" alt="Product Image">
<div class="card-body">
<h4>产品标题</h4>
<p>这是一段产品描述文字。</p>
</div>
<div class="badge">Hot</div>
</div>

固定定位 (fixed)

position: fixed; 会让元素脱离文档流,并相对于浏览器视口进行定位。这意味着即使用户滚动页面,该元素的位置也永远不会改变。

实战: “返回顶部”按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html {
/* 开启平滑向上的关键属性 */
scroll-behavior: smooth;
}

h1 {
height: 1000px;
background-color: #2980b9;
color: white;
display: grid;
place-items: center;
}

.placeholder {
height: 2000px;
}

.backTop {
position: fixed;
/* 相对于视口固定 */
bottom: 30px;
right: 30px;
width: 50px;
height: 50px;
background-color: #2980b9;
color: white;
display: grid;
place-items: center;
border-radius: 50%;
text-decoration: none;
}

</style>
</head>

<body>
<h1>顶部区域</h1>
<div class="placeholder"></div>
<a href="#" class="backTop"></a>
</body>

</html>

6.3.4. 综合实战:创建一个居中模态框 (Modal)

任务: 综合运用 position: fixedz-index,创建一个覆盖全屏的半透明遮罩层,并在其上居中显示一个模态框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.modal-overlay {
position: fixed;
/* 固定定位,覆盖整个视口 */
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
/* 半透明黑色遮罩 */
z-index: 1000;
/* 确保在最顶层 */

/* 使用 Flexbox 实现完美的垂直水平居中 */
display: flex;
justify-content: center;
align-items: center;
}

.modal-content {
background-color: white;
padding: 30px;
border-radius: 8px;
width: 80%;
max-width: 500px;
text-align: center;
}


.modal-overlay {
display: none;
}
</style>
</head>

<body>

<button class="open-modal">打开模态框</button>
<div class="modal-overlay">
<div class="modal-content">
<h3>这是一个模态框</h3>
<p>这里是模态框的内容。遮罩层使用了 position: fixed,模态框本身通过 Flexbox 在遮罩层内居中。</p>
<button>关闭</button>
</div>
</div>
<script>
// 简单的写几行Js代码来打开我们的模态框
const openModal = document.querySelector('.open-modal');
const modalOverlay = document.querySelector('.modal-overlay');
openModal.addEventListener('click', () => {
modalOverlay.style.display = 'flex';
});
</script>
</body>

</html>

6.4. 多列布局 (Multi-column):优雅实现内容分栏与瀑布流

摘要: 本节我们将学习一个巧妙而高效的布局工具——多列布局。您将学习如何像排版报纸和杂志一样,轻松地将大段文本内容分割成多列以提升可读性。更重要的是,我们将通过一个实战案例,向您展示如何仅用三行核心 CSS 代码就实现一个完美的、响应式的图片瀑布流布局,这是一个在现代网页设计中非常流行且实用的技巧。

6.4.1. 核心属性详解

多列布局的所有属性都应用于容器元素,它会将容器内的所有子元素(无论块级还是行内)自动分配到各列中。

属性核心描述
column-count固定列的数量。
column-width定义每列的理想宽度,浏览器会据此自动调整列数,实现响应式。
columnscountwidth 的简写属性。
column-gap定义列与列之间的间距。
column-rule定义列与列之间的分隔线样式,语法与 border 类似。
column-span:all让标题横跨所有列
break-inside:avoid避免在元素内部断开换行,让元素尽量完整显示在同一列。

深入讲解 column-countcolumn-width

这两个属性是多列布局的核心,通常我们只使用其中一个来驱动布局:

  • column-count (固定列数): 当你希望布局始终保持特定数量的列(例如,一个三栏文章),无论容器宽度如何变化时,使用此属性。
  • column-width (自适应列数): 当你希望布局响应式地变化时,使用此属性。你只需定义一个“理想”的列宽(例如 200px),浏览器会自动计算在当前容器宽度下,能容纳多少个这样的列。当容器变宽,列数会自动增加;容器变窄,列数会自动减少。这是实现响应式瀑布流的关键。

6.4.2. 实战应用

实战一:报纸式文章排版

任务: 将一段长文本自动格式化为三列,并在列与列之间添加一条浅灰色分隔线,同时让文章标题横跨所有列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<style>
.newspaper-article {
width: 90%;
margin: auto;
border: 1px solid #ddd;
padding: 20px;

/* 1. 固定为 3 列 */
column-count: 3;
/* 2. 设置列间距 */
column-gap: 40px;
/* 3. 设置列间的分隔线 */
column-rule: 1px solid #eee;
}
.newspaper-article h2 {
/* 4. 让标题横跨所有列 */
column-span: all;
text-align: center;
margin-top: 0;
margin-bottom: 20px;
}
.newspaper-article p {
text-align: justify;
line-height: 1.7;
}
</style>
<article class="newspaper-article">
<h2>CSS 多列布局的艺术</h2>
<p>多列布局是一个经常被低估的现代布局工具,它可以轻松地将一段连续的内容(如文本)分割成报纸或杂志那样的多列。这种布局方式不仅美观,还能显著提高长篇文本的可读性,因为它避免了用户视线需要进行过长的水平移动。</p>
<p>要使用多列布局,你只需在容器元素上应用几个简单的 CSS 属性。最核心的属性是 column-count 和 column-width。前者用于定义固定的列数,而后者则定义一个理想的列宽,浏览器会根据容器的可用空间自动计算出合适的列数,从而轻松实现响应式效果。这使得多列布局也成为创建图片瀑布流的一种极其简单高效的方法。</p>
<p>除了基本的列数和宽度,我们还可以通过 column-gap 来控制列与列之间的间距,使用 column-rule 来在列间添加装饰性的分隔线。更有趣的是,通过 column-span 属性,我们可以让某个子元素(比如一个大标题或者一张关键图片)横跨所有列,打破单调的布局,创造出更富吸引力的视觉焦点。</p>
</article>

实战二:纯 CSS 图片瀑布流

任务: 创建一个响应式的图片瀑布流布局。图片会根据浏览器窗口宽度自动重新排列成不同的列数,并且每张图片都保持完整,不会被列分割。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.waterfall-container {
/* 1. 定义一个理想的列宽,浏览器会据此自动计算列数,实现响应式 */
column-width: 220px;
/* 2. 定义列间距 */
column-gap: 15px;
width: 90%;
margin: auto;
}

.waterfall-item {
width: 100%;
/* 图片宽度充满其所在列 */
height: auto;
/* 高度自适应 */
margin-bottom: 15px;
border-radius: 8px;
/* 3. 核心:防止图片在换列时被从中间截断 */
break-inside: avoid;
display: block;
/* 确保图片是块级元素,消除底部幽灵空白 */
}
</style>
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
</head>

<body>
<div class="waterfall-container" x-data="{
images: [
{ seed: 'fall1', height: 600 },
{ seed: 'fall2', height: 500 },
{ seed: 'fall3', height: 650 },
{ seed: 'fall4', height: 450 },
{ seed: 'fall5', height: 700 },
{ seed: 'fall6', height: 520 },
{ seed: 'fall7', height: 580 },
{ seed: 'fall8', height: 480 },
{ seed: 'fall9', height: 620 },
{ seed: 'fall10', height: 540 },
{ seed: 'fall11', height: 680 },
{ seed: 'fall12', height: 420 },
{ seed: 'fall13', height: 750 },
{ seed: 'fall14', height: 460 },
{ seed: 'fall15', height: 590 },
{ seed: 'fall16', height: 510 },
{ seed: 'fall17', height: 640 },
{ seed: 'fall18', height: 470 },
{ seed: 'fall19', height: 720 },
{ seed: 'fall20', height: 550 },
{ seed: 'fall21', height: 610 },
{ seed: 'fall22', height: 490 },
{ seed: 'fall23', height: 660 },
{ seed: 'fall24', height: 430 }
]
}">
<template x-for="image in images" :key="image.seed">
<img class="waterfall-item"
:src="`https://picsum.photos/seed/${image.seed}/400/${image.height}`">
</template>
</div>
</body>

</html>

请尝试在此将代码复制到Vs Code中,启用 F12 浏览器 拉动浏览器窗口查看效果


6.5. 响应式设计:让布局适应万千屏幕

摘要: 本节我们将学习现代网页开发的基石——响应式设计。这并非一种新的布局技术,而是一种设计思想与方法论。我们将学习其三大核心支柱:流式网格、弹性图片和媒体查询。通过掌握这些,您将能够让一套代码在手机、平板和桌面电脑上呈现出截然不同却都同样完美的布局效果,为所有用户提供最佳的浏览体验。

6.5.1. 核心概念与基石

响应式设计的实现依赖于几项关键的 CSS 技术和开发思想。

视口 (Viewport) 元标签

这是开启移动端适配的必备第一步,是响应式设计的“准生证”。这行代码必须被添加到 HTML 文件的 <head> 标签中。

1
<meta name="viewport" content="width=device-width, initial-scale=1.0">

它告诉移动端浏览器:

  • width=device-width: 将视口的宽度设置为设备的屏幕宽度。
  • initial-scale=1.0: 页面的初始缩放比例为 1.0,即不缩放。

如果没有这行代码,移动端浏览器会默认使用一个桌面宽度的“虚拟视口”(通常是 980px)来渲染页面,然后将整个页面缩小,导致文字变得极小,布局混乱。

媒体查询 (Media Queries)

媒体查询是响应式设计的技术核心,它像一个 CSS 的 if 语句,允许我们为满足特定条件的设备应用特定的样式。

语法: @media (媒体特性) { /* 在此条件满足时应用的 CSS 规则 */ }

常用媒体特性描述示例
min-width当视口宽度 大于或等于 指定值时应用样式。@media (min-width: 768px)
max-width当视口宽度 小于或等于 指定值时应用样式。@media (max-width: 767px)
orientationportrait (竖屏) 或 landscape (横屏)。@media (orientation: landscape)

设计思想:移动优先

这是一种重要的现代响应式设计策略。我们不再先为大屏幕设计,再去“修补”小屏幕的样式。而是反过来:

  1. 先编写基础样式: 这些样式服务于最小的设备(手机),保证核心功能和内容的完美呈现。这些代码写在所有 @media 规则之外。
  2. 逐步增强: 使用 min-width 媒体查询,为更大的屏幕(如平板、桌面)追加更复杂的样式和布局。
    “移动优先”的优势:
  • 性能更佳: 移动设备只需加载最核心、最轻量的 CSS。
  • 代码更清晰: 避免了在媒体查询中大量重置和覆盖桌面端样式,代码逻辑是不断“增加”而非“修改”。
  • 体验更专注: 强迫我们优先考虑移动端用户的核心体验。

6.5.2. 断点的选择

“断点”是指布局发生显著变化的屏幕宽度临界点。选择哪些断点并没有绝对的标准,最佳实践是根据你的内容来决定在何时需要改变布局。不过,业界通常会参考一些主流 UI 框架(如 Bootstrap)的约定,这有助于团队协作。

一套常用的断点参考:

设备类型断点描述
手机 (Mobile)< 768px默认样式,通常为单列布局。
平板 (Tablet)≥ 768px布局开始变为多列,空间更宽裕。
桌面 (Desktop)≥ 992px经典的桌面端宽屏布局。
大桌面 (Large Desktop)≥ 1200px为超宽屏幕提供更丰富的布局。

6.5.3. 实战:将 Grid 首页布局改造为响应式

任务: 我们将以 6.2.3 节中创建的经典网站布局为基础,采用“移动优先”的策略,为其添加响应式能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式 Grid 布局</title>
<style>
body { margin: 0; font-family: sans-serif; }
.responsive-layout {
display: grid;
min-height: 100vh;
gap: 10px;

/* 默认样式 (移动优先):
这是为小于 768px 的屏幕准备的单列布局。
它在所有设备上都会生效,除非被后续的媒体查询覆盖。
*/
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
grid-template-columns: 1fr;
grid-template-rows: 60px 1fr auto 60px;
}

/* 断点 1:平板及以上设备
当屏幕宽度大于等于 768px 时,应用以下规则,
覆盖掉默认的移动端布局。
*/
@media (min-width: 768px) {
.responsive-layout {
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-rows: 60px 1fr 60px;
grid-template-columns: 200px 1fr;
}
}

/* 以下是各区域的样式,无需改变 */
.r-header { grid-area: header; background-color: #add8e6; }
.r-sidebar { grid-area: sidebar; background-color: #b0e0e6; }
.r-main { grid-area: main; background-color: #f0f8ff; }
.r-footer { grid-area: footer; background-color: #add8e6; }
.r-header, .r-sidebar, .r-main, .r-footer { display: grid; place-items: center; font-size: 20px;}
</style>
</head>
<body>
<div class="responsive-layout">
<header class="r-header">Header</header>
<aside class="r-sidebar">Sidebar</aside>
<main class="r-main">Main Content</main>
<footer class="r-footer">Footer</footer>
</div>
</body>
</html>

请尝试在此将代码复制到Vs Code中,启用 F12 浏览器 拉动浏览器窗口查看效果


6.6. 最后的拼图:理解 Float

摘要: 在本章的最后,我们将回顾一个在 CSS 历史上扮演过重要角色、但如今已回归其本职的属性——float。本节的目标不是让您用它来构建复杂的布局,恰恰相反,是让您理解它的唯一正确应用场景(图文环绕),并能看懂和维护那些使用浮动布局的“历史遗留”代码。我们将重点讲解浮动带来的“高度塌陷”问题以及经典的“清除浮动”解决方案。

6.6.1. Float 的唯一正确用途:图文环绕

float 属性最初被设计出来的唯一目的,就是为了实现类似报纸、杂志中的图文环绕排版效果。通过设置 float: left;float: right;,可以让一个元素(通常是图片)脱离正常的文档流,移动到其容器的左侧或右侧,其后的文本内容则会环绕在该元素的周围。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style>
.article-content {
width: 80%;
margin: auto;
border: 1px solid #ccc;
padding: 20px;
font-family: sans-serif;
line-height: 1.7;
}
.article-content img {
/* 1. 让图片向左浮动 */
float: left;
/* 2. 给图片右侧和下方一些间距,避免文字紧贴 */
margin-right: 20px;
margin-bottom: 10px;
border-radius: 8px;
}
</style>
<div class="article-content">
<img src="https://picsum.photos/seed/float-demo/200/200" alt="Scenery">
<p>这段文字将优雅地环绕在图片的周围。浮动(Float)最初就是为了实现这种类似报纸和杂志的图文混排效果而设计的。在现代 CSS 布局中,除了这个场景,我们几乎不再需要使用它。当一个元素浮动后,它会部分地脱离文档流,允许其他行内内容(如文本)围绕它流动。这是它最核心、也是如今唯一推荐的用途。</p>
<p>然而,开发者们曾“滥用”浮动来实现多列布局,但这会带来一系列的副作用,其中最著名的就是“高度塌陷”问题,我们将在下一节探讨它。</p>
</div>

6.6.2. 历史遗留问题与解决方案:清除浮动

问题:高度塌陷 (Height Collapse)

痛点背景:当一个父元素内部的所有子元素都设置了浮动时,这些子元素会完全脱离常规文档流。对于父元素来说,它会认为自己“内部没有任何内容”,因此它的高度就会“塌陷”为 0。这将彻底打乱后续元素的布局。

直观演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<style>
.parent-box {
border: 3px solid #e74c3c;
width: 100%;
}
.parent-box .child {
float: left;
width: 100px;
height: 100px;
background: #fbe9e7;
margin: 10px;
}
.next-element {
margin-top: 20px;
background: #e3f2fd;
padding: 10px;
}
</style>
<div class="parent-box">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
</div>
<div class="next-element">
我是父元素后面的元素。看,父元素的红色边框塌陷了,我的位置也乱了。
</div>

解决方案:清除浮动 (Clearfix)

为了解决高度塌陷问题,开发者们创造了多种“清除浮动”的方法。在 2025 年的今天,最常用且最标准的方法是 Clearfix Hack

它的原理是:利用 ::after 伪元素,在父元素的内容之后添加一个看不见的、块级的、并且设置了 clear: both; 的虚拟元素。clear: both; 这条规则会命令这个虚拟元素“移动到所有浮动元素的下方”,从而迫使父元素必须将高度延伸到这个虚拟元素所在的位置,也就奇迹般地“撑开”了父元素。

标准 Clearfix 代码:

1
2
3
4
5
6
7
.clearfix::after {
content: ""; /* 必须有 content 属性 */
display: block; /* 必须是块级元素 */
clear: both; /* 核心:清除两侧的浮动 */
height: 0; /* 确保它不产生额外高度 */
visibility: hidden; /* 让它不可见 */
}

修复后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<style>
/* 引入 Clearfix 解决方案 */
.clearfix::after {
content: "";
display: block;
clear: both;
}
.parent-box-fixed {
border: 3px solid #2ecc71;
width: 100%;
}
.parent-box-fixed .child {
float: left;
width: 100px;
height: 100px;
background: #e8f5e9;
margin: 10px;
}
.next-element-fixed {
margin-top: 20px;
background: #e3f2fd;
padding: 10px;
}
</style>
<div class="parent-box-fixed clearfix">
<div class="child">Child 1</div>
<div class="child">Child 2</div>
</div>
<div class="next-element-fixed">
现在父元素的绿色边框正常了,我的位置也正确了。
</div>

现代视角: Clearfix 的整个概念虽然巧妙,但也体现了使用 float 进行布局的复杂性和脆弱性。这正是我们现在拥抱 FlexboxGrid 的根本原因——它们从设计上就避免了这类问题,让布局变得简单、直观和健壮。


第七章:CSS 视觉魔法:从阴影、渐变到滤镜

摘要: 恭喜您已经掌握了 CSS 布局!在本章中,我们将开启一段充满创造力的旅程,深入探索 CSS 提供的丰富视觉效果。您将学习如何控制元素的可见性与透明度,如何使用阴影为页面增添立体感,如何用渐变创造绚丽的背景,以及如何像使用 Photoshop 一样,通过滤镜 (filter) 实时处理元素的视觉表现。本章将是您从“结构工程师”向“视觉设计师”转变的关键一步。


在本章的学习地图中,我们将:

  1. 首先,学习如何控制元素的显隐与透明,这是实现动态 UI 的基础。
  2. 接着,我们将掌握阴影效果,为元素和文本赋予深度和立体感。
  3. 然后,我们将深入探索渐变背景,创造出比单色背景更丰富、更具吸引力的视觉效果。
  4. 之后,我们将解锁强大的 filter (滤镜) 属性,实现以往需要制图软件才能完成的特效。
  5. 最后,我们会学习一些其他实用的 UI 属性,并用一个综合实战案例来巩固所有知识。

7.1. 显隐与透明:控制元素状态

7.1.1. visibility 属性

visibility 属性用于设置元素是否可见。它与 display: none; 的核心区别在于:

  • visibility: hidden;: 元素被隐藏,但在页面布局中依然占据着它原来的空间,就像一个“隐形”的占位符。
  • display: none;: 元素被彻底隐藏,并且从文档流中移除,不占据任何空间。
描述
visible默认值,元素可见。
hidden元素不可见,但仍占据空间。
collapse主要用于表格行/列,效果类似 hidden
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式 Grid 布局</title>
<style>
.visibility-demo { border: 2px dashed #ccc; padding: 10px; }
.visibility-demo div { display: inline-block; width: 100px; height: 100px; background: #add8e6; }
.hide-by-visibility { visibility: hidden; }
/* 提高优先级 */
.hide-by-display { display: none !important; }
</style>
</head>

<body>
<strong>visibility: hidden (中间盒子隐身但仍占位)</strong>
<div class="visibility-demo">
<div>Box 1</div>
<div class="hide-by-visibility">Box 2</div>
<div>Box 3</div>
</div>
<br><br>
<strong>display: none (中间盒子彻底消失)</strong>
<div class="visibility-demo">
<div>Box 1</div>
<div class="hide-by-display">Box 2</div>
<div>Box 3</div>
</div>
</body>

</html>

7.1.2. opacity 属性

opacity 属性用于设置一个元素的透明度,值从 0.0 (完全透明) 到 1.0 (完全不透明)。

重要: opacity 会影响元素本身及其所有子元素。如果你只想让背景半透明而文字保持清晰,应该在第五章学过的 background-color: rgba(...)

1
2
3
4
5
6
7
8
9
10
11
12
<style>
.opacity-demo {
background-color: #2980b9;
color: white;
padding: 20px;
opacity: 0.6; /* 整个元素,包括文字,都变为 60% 透明度 */
}
</style>
<div class="opacity-demo">
<h3>标题和段落</h3>
<p>这段文字也继承了父元素的透明度,变得有些模糊。</p>
</div>

7.2. 阴影效果:box-shadowtext-shadow

7.2.1. 盒子阴影 (box-shadow)

为元素添加阴影是增加页面深度感和层次感最有效的方式之一。
语法: box-shadow: <h-offset> <v-offset> <blur> <spread> <color> <inset>;

  • h-offset: 水平偏移量 (正值向右)。
  • v-offset: 垂直偏移量 (正值向下)。
  • blur: 模糊半径 (值越大越模糊)。
  • spread: 扩散半径 (正值使阴影扩大)。
  • color: 阴影颜色。
  • inset: 将外部阴影变为内部阴影。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<style>
.shadow-container { display: flex; justify-content: space-around; padding: 20px; background-color: #f4f4f4; }
.shadow-box { width: 100px; height: 100px; background-color: white; display: grid; place-items: center; }
.s1 { box-shadow: 5px 5px 0px 0px #aaa; }
.s2 { box-shadow: 5px 5px 15px 0px #aaa; }
.s3 { box-shadow: 0px 0px 15px 5px #aaa; }
.s4 { box-shadow: inset 0px 0px 15px 0px #aaa; }
</style>
<div class="shadow-container">
<div class="shadow-box s1">硬阴影</div>
<div class="shadow-box s2">模糊阴影</div>
<div class="shadow-box s3">扩散阴影</div>
<div class="shadow-box s4">内阴影</div>
</div>

7.2.2. 文本阴影 (text-shadow)

text-shadow 的语法更简单,但通过叠加多层阴影,可以创造出惊艳的效果。
语法: text-shadow: <h-offset> <v-offset> <blur> <color>;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<style>
.text-shadow-demo h1 { font-size: 50px; text-align: center; padding: 20px; font-family: sans-serif; }
.ts1 {
background: #333;
color: white;
text-shadow: 2px 2px 5px black;
}
.ts2 {
background: #2c3e50;
color: #4dd0e1;
/* 通过叠加多层阴影创造霓虹灯效果 */
text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 20px #00aeff, 0 0 30px #00aeff;
}
</style>
<div class="text-shadow-demo">
<h1 class="ts1">简单文本阴影</h1>
<h1 class="ts2">霓虹灯效果</h1>
</div>

7.3. 渐变背景 (Gradients)

使用 CSS 渐变可以创建平滑的颜色过渡,替代过去需要用图片才能实现的背景效果。

7.3.1. 线性渐变 (linear-gradient)

沿直线创建颜色渐变。

1
2
3
4
5
6
7
8
9
10
11
<style>
.gradient-box { width: 22%; height: 100px; float: left; margin: 1.5%; }
.lg1 { background: linear-gradient(#3498db, #2980b9); }
.lg2 { background: linear-gradient(to right, #e74c3c, #c0392b); }
.lg3 { background: linear-gradient(45deg, #f1c40f, #f39c12); }
.lg4 { background: linear-gradient(to right, #e74c3c, #f1c40f, #2ecc71); }
</style>
<div class="gradient-box lg1"></div>
<div class="gradient-box lg2"></div>
<div class="gradient-box lg3"></div>
<div class="gradient-box lg4"></div>

其他的渐变语法我们开发中不会太常用,为了避免知识点罗列我们就略过了


7.4. 滤镜 (filter):CSS 中的 Photoshop

filter 属性可以将各种图形效果应用于元素,就像在 Photoshop 中使用滤镜一样。

函数核心描述
blur(px)高斯模糊
brightness(%)亮度
contrast(%)对比度
grayscale(%)灰度(黑白)
invert(%)反相
saturate(%)饱和度
sepia(%)褐色(复古)
hue-rotate(deg)色相旋转
drop-shadow(...)(常用) 投射阴影

7.4.1. 实战:创建交互式图片效果

任务: 创建一个图片廊,默认显示为灰度图片,当鼠标悬停时,平滑地恢复原彩并放大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.gallery img {
width: 200px;
margin: 10px;
border-radius: 8px;
/* 1. 默认应用灰度滤镜 */
filter: grayscale(100%);
/* 2. 添加过渡效果,让变化更平滑 */
transition: all 0.3s ease-in-out;
}
.gallery img:hover {
/* 3. 鼠标悬停时,移除滤镜并放大 */
filter: grayscale(0%);
transform: scale(1.1);
}
</style>
<div class="gallery">
<img src="https://picsum.photos/seed/gallery1/400/300">
<img src="https://picsum.photos/seed/gallery2/400/300">
<img src="https://picsum.photos/seed/gallery3/400/300">
</div>

7.4.2. drop-shadow() vs box-shadow

这是两个非常容易混淆的属性,但区别巨大:

  • box-shadow: 为元素的矩形边框盒添加阴影。
  • filter: drop-shadow(): 为元素内容的实际轮廓(包括 PNG 的透明部分)智能地添加阴影。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>响应式 Grid 布局</title>
<style>
.shadow-compare { display: flex; justify-content: space-around; align-items: center; background: #f4f4f4; padding: 20px; }
.shadow-compare img { width: 100px; }
.bs-shadow {
/* 阴影作用于整个矩形盒子 */
box-shadow: 10px 10px 10px rgba(0,0,0,0.5);
}
.ds-shadow {
/* 阴影智能地贴合了图片的轮廓 */
filter: drop-shadow(10px 10px 10px rgba(0,0,0,0.5));
}
</style>
</head>

<body>
<div class="shadow-compare">
<div>
<p>box-shadow:</p>
<img class="bs-shadow" src="https://picsum.photos/100/100?random=1">
</div>
<div>
<p>drop-shadow:</p>
<img class="ds-shadow" src="https://picsum.photos/100/100?random=2">
</div>
</div>
</body>

</html>

7.5. 裁剪与蒙版:塑造元素的可见区域

摘要: 本节我们将学习两个强大的视觉特效属性:clip-pathmask。您将学习如何使用 clip-path 将元素裁剪成圆形、多边形等任意形状,打破矩形的束缚。同时,我们还将探索如何利用 mask-image,通过一张图片或渐变来控制元素的像素级可见性,创造出丰富的淡入淡出和镂空效果。

7.5.1. 裁剪路径 (clip-path)

clip-path 属性通过定义一个裁剪区域,来决定元素的哪一部分是可见的。区域内的部分会被显示,区域外的部分则会被彻底“剪掉”,变得不可见。

在旧的 CSS 版本中曾有一个 clip 属性,但它功能有限且已被废弃。clip-path 是其现代的、功能强大的替代品。

函数核心描述 & 参数解析
circle()创建圆形。参数为 (半径 at 圆心位置)
示例: circle(50% at center)
ellipse()创建椭圆形。参数为 (水平半径 垂直半径 at 中心位置)
示例: ellipse(50% 30% at center)
inset()创建内凹矩形。参数为 (内凹值 round 圆角)
内凹值遵循 margin 简写,round 可选。
示例: inset(10% 10% round 20px)
polygon()(最强大) 用一系列 X Y 坐标点创建任意多边形
至少需要三个点。
示例 (三角形): polygon(50% 0, 100% 100%, 0 100%)
url()使用一个外部 SVG <clipPath> 元素的 id 作为裁剪路径。
用于实现复杂图形。
示例: url(#my-clip-path)
  • 语法: polygon(x1 y1, x2 y2, x3 y3, …)
  • 坐标点: 每一对 x y 值都代表多边形的一个顶点(角)。坐标 0 0 是左上角。
  • 至少需要 3 对坐标点才能构成一个面(三角形)。
  • 浏览器会自动将最后一个点与第一个点连接起来,形成一个封闭的图形

对于polygon的使用,强烈推荐您收藏 Clippy — CSS clip-path 生成器 网站,他能够快速的帮你创建您几乎所有想创建的图形


实战: 将同一张图片裁剪成不同的形状。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<style>
.clip-gallery {
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
gap: 20px;
}
.clip-gallery img {
width: 150px;
height: 150px;
object-fit: cover; /* 确保图片不变形 */
transition: all 0.3s ease;
}
.clip-circle {
clip-path: circle(50%);
}
.clip-inset {
clip-path: inset(10% 10% round 20px);
}
.clip-polygon {
/* 一个简单的菱形 */
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.clip-gallery img:hover {
transform: scale(1.1);
filter: brightness(1.1);
}
</style>
<div class="clip-gallery">
<div>
<p>圆形 (circle)</p>
<img class="clip-circle" src="https://picsum.photos/seed/clip/300/300">
</div>
<div>
<p>内凹 (inset)</p>
<img class="clip-inset" src="https://picsum.photos/seed/clip/300/300">
</div>
<div>
<p>多边形 (polygon)</p>
<img class="clip-polygon" src="https://picsum.photos/seed/clip/300/300">
</div>
</div>

7.5.2. 蒙版 (mask):像素级的视觉控制器

clip-path 的硬性裁剪不同,mask 提供了一种更精细、更具艺术性的方式来控制元素的可见性。它不是“剪掉”,而是像透过一块有图案的毛玻璃看东西,玻璃上透明或亮的地方,我们就能看到东西;不透明或暗的地方,东西就被遮挡了。

核心 mask 属性三巨头

在使用蒙版时,我们最先需要掌握的是这三个最核心的属性,它们决定了蒙版的基础形态。

  1. mask-image (定义蒙版源): 这是最重要的属性,它指定了用作“毛玻璃”的图案。这个图案可以是一张带透明通道的 url() 图片,也可以是一个 CSS 渐变。
  2. mask-size (控制蒙版尺寸): 控制蒙版图案的大小,用法与 background-size 完全相同,containcover 是最常用的值。
  3. mask-repeat (控制蒙版平铺): 控制蒙版图案是否重复,用法与 background-repeat 完全相同。对于形状遮罩,我们通常使用 no-repeat
实战一:SVG 形状蒙版

任务: 将一张头像图片,通过一个五角星形状的 SVG 文件进行遮罩,使其呈现为五角星形状。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style>
.star-mask {
width: 300px;
height: 300px;
}
.star-mask img {
width: 100%;
height: 100%;
object-fit: cover;

/* 1. 使用 SVG 文件作为蒙版图像 */
-webkit-mask-image: url('https://mdn.github.io/css-examples/masking/star.svg');
mask-image: url('https://mdn.github.io/css-examples/masking/star.svg');

/* 2. 确保蒙版居中、不重复且完整显示 */
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: contain;
mask-size: contain;
}
</style>
<div class="star-mask">
<img src="https://q7.itc.cn/q_70/images03/20240614/e18a3ca14f82467ba63e93c1cce681ee.jpeg" alt="Profile Picture" />
</div>
实战二:渐变蒙版 (淡出效果)

这是 mask 最巧妙的应用之一。我们无需任何图片,仅用 CSS 渐变就可以创造出优雅的淡出效果。

任务: 为一张图片的底部创造出“淡出至透明”的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
<style>
.fade-out-mask {
width: 300px;
height: 200px;
background-image: url(https://picsum.photos/seed/gradient-mask/400/300);
background-size: cover;

/* 核心:创建一个从黑色(代表完全可见)到底部透明(代表完全不可见)的渐变蒙版 */
-webkit-mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
}
</style>
<div class="fade-out-mask"></div>

微调与进阶属性详解

在掌握了 mask-image 等核心属性后,我们可以通过以下属性,像控制背景图一样,对蒙版进行更精细的调整。


1. mask-position: 定义蒙版位置

此属性用于设置 mask-image 在元素内的起始位置,其用法与 background-position 完全相同。

描述
关键字top, bottom, left, right, center 的组合
<percentage> / <length>使用百分比或 px 等单位设置 X Y 坐标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<style>
.position-demo {
width: 300px;
height: 200px;
background-image: linear-gradient(45deg, #84fab0 0%, #8fd3f4 100%);

/* 使用一个较小的 SVG 作为蒙版图 */
-webkit-mask-image: url('https://mdn.github.io/css-examples/masking/star.svg');
mask-image: url('https://mdn.github.io/css-examples/masking/star.svg');

-webkit-mask-size: 80px 80px; /* 设置蒙版图尺寸 */
mask-size: 80px 80px;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;

/* 核心:将蒙版图定位到右上角 */
-webkit-mask-position: top right;
mask-position: top right;
}
</style>
<div class="position-demo"></div>

观察星形蒙版是如何被精确定位到容器右上角的。


2. mask-originmask-clip: 定义蒙版原点与裁剪区域

这两个属性共同决定了蒙版的作用范围,它们的参照物都是元素的盒模型(border-box, padding-box, content-box)。

  • mask-origin: 定义 mask-position0 0 坐标从哪里开始计算
  • mask-clip: 定义蒙版效果在哪里被裁剪
描述
border-box默认值。从边框的外边缘开始计算/裁剪。
padding-box从内边距的外边缘(即边框的内边缘)开始计算/裁剪。
content-box从内容区的边缘开始计算/裁剪。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<style>
.box-model-mask {
width: 250px;
height: 150px;
padding: 20px;
border: 10px dashed #ccc;
background-color: #3498db;

-webkit-mask-image: linear-gradient(45deg, black, transparent);
mask-image: linear-gradient(45deg, black, transparent);

/* 尝试修改下面这两个属性的值 */
-webkit-mask-origin: padding-box;
mask-origin: padding-box;
-webkit-mask-clip: content-box;
mask-clip: content-box;
}
</style>
<div class="box-model-mask"></div>

在示例中,mask-origin: padding-box 让渐变蒙版的左上角从内边距区域开始,而 mask-clip: content-box 则让蒙版的效果只在内容区内生效,不影响内边距和边框。


7.6. 其他实用 UI 属性

7.6.1. resize 属性

允许用户通过拖动来缩放元素的尺寸。

resize 属性需要与 overflow 属性(值为 auto, hiddenscroll)结合使用才有效。

1
2
3
4
5
6
7
8
9
10
11
<style>
textarea.resizable {
resize: both; /* 允许用户在水平和垂直方向上调整大小 */
overflow: auto;
border: 1px solid #ccc;
padding: 5px;
width: 300px;
height: 100px;
}
</style>
<textarea class="resizable">你可以拖动右下角来调整我的大小。</textarea>

7.6.2. cursor 属性:改变鼠标指针

cursor 属性用于定义当鼠标指针悬停在元素上时所显示的图标样式。为可交互元素设置正确的鼠标指针,是提升用户体验、提供明确视觉线索的关键。在浏览网页的过程中,当我们将鼠标移动到一些元素上时,鼠标的样式会发生相应的改变,例如当鼠标指向文本时,鼠标的样式会变成类似大写字母I的样子;当鼠标指向链接时,鼠标会变成一个小手的形状等。

除了这些默认的变化外,您还可以通过 CSS 中的 cursor 属性来改变网页中鼠标(光标)的样式,下表中列举了 cursor 属性的可选值:

属性值示意图描述
auto默认值,由浏览器根据当前上下文确定要显示的光标样式
default默认光标默认光标,不考虑上下文,通常是一个箭头
none不显示光标
initial将此属性设置为其默认值
inherit从父元素基础 cursor 属性的值
context-menu上下文菜单光标表示上下文菜单可用
help帮助光标表示有帮助
pointer指针光标表示一个链接
progress进度游标进度指示器,表示程序正在执行一些处理,但是您仍然可以在该界面进行一些操作(与 wait 不同)
wait等待光标表示程序繁忙,您应该等待程序指向完成
cell单元游标表示可以选择一个单元格(或一组单元格)
crosshair十字准线光标一个简单的十字准线
text文字游标表示可以选择的文本
vertical-text垂直文本光标表示可以选择的垂直文本
alias别名光标表示要创建别名或快捷方式
copy复制光标表示可以复制某些内容
move移动光标表示可以移动鼠标下方的对象
no-drop无丢游标表示所拖动的项目不能放置在当前位置
not-allowed不允许的游标表示无法完成某事
all-scroll全滚动光标表示对象可以沿任何方向滚动(平移)
col-resize彩色游标表示可以水平调整列的大小
row-resize行大小调整游标表示可以垂直调整行的大小
n-resizeN尺寸游标表示对象的边缘可以向上(向北)移动
e-resize电子调整游标表示对象的边缘可以向右(向东)移动
s-resizeS调整游标表示对象的边缘可以向下(向南)移动
w-resizeW尺寸游标表示对象的边缘可以向左(向西)移动
ne-resizeNE调整大小的游标表示对象的边缘可以向上和向右(北/东)移动
nw-resizeNW调整游标表示对象的边缘可以向上和向左(北/西)移动
se-resizeSE调整游标表示对象的边缘可以向下和向右(向南/向东)移动
sw-resizeSW调整游标表示对象的边缘可以向下和向左(南/西)移动
ew-resizeEW调整游标表示可以双向调整对象大小的光标
ns-resizeNS调整大小的游标
nesw-resizeNESW调整大小的游标
nwse-resizeNWSE调整大小的游标
zoom-in放大光标表示可以放大某些内容
zoom-out缩小光标表示可以缩小某些内容
grab抓取光标表示可以抓取(拖动)某些东西
grabbing抓取光标表示已经抓取到某些东西
url(“”)自定义光标的样式,括号中的内容为光标图像的源文件路径

自定义光标样式

除了可以使用上表中介绍的光标样式外,您也可以使用图像文件来自定义光标的样式,如下所示:

1
2
cursor: url("custom.gif"), url("custom.cur"), default;

提示:.cur 格式是光标文件的标准格式,您可以使用一些在线工具(例如 https://convertio.co/zh/cur-converter/)将 .jpg、.gif 等格式的图像文件转换为 .cur 格式。


实战: 为不同功能的元素设置对应的鼠标指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<style>
.cursor-demo-area {
display: flex;
flex-wrap: wrap;
gap: 15px;
font-family: sans-serif;
}
.cursor-box {
width: 120px;
height: 80px;
border: 1px solid #ccc;
border-radius: 5px;
display: grid;
place-items: center;
background: #f9f9f9;
}

.c-pointer { cursor: pointer; background: #e3f2fd; }
.c-help { cursor: help; }
.c-move { cursor: move; }
.c-not-allowed { cursor: not-allowed; color: #999; background: #eee; }
.c-text { cursor: text; }
.c-wait { cursor: wait; }
</style>
<div class="cursor-demo-area">
<div class="cursor-box c-pointer">pointer</div>
<div class="cursor-box c-help">help</div>
<div class="cursor-box c-move">move</div>
<div class="cursor-box c-not-allowed">not-allowed</div>
<div class="cursor-box c-text">text</div>
<div class="cursor-box c-wait">wait</div>
</div>

将鼠标依次悬停在上面的方块上,观察指针的变化。


7.7. 本章核心速查总结与实战

分类关键项核心描述
显隐visibility: hidden隐藏元素,但保留其布局空间。
display: none隐藏元素,并移除其布局空间。
透明度opacity: 0.5整个元素(包括子元素)都变半透明。
background: rgba()(推荐) 仅背景半透明。
阴影box-shadow为元素的矩形盒子添加阴影。
filter: drop-shadow()为元素内容的实际轮廓添加阴影。
渐变linear-gradient()线性渐变。
radial-gradient()径向渐变。
滤镜filterblur(), grayscale() 等,实现 Photoshop 级特效。

综合实战:创建“毛玻璃”效果卡片

“毛玻璃” (Glassmorphism) 是现代 UI 设计中非常流行的效果,它模拟了透过磨砂玻璃看背景的观感。
核心属性: backdrop-filter

backdrop-filter 是一个特殊的属性,它不对元素本身应用滤镜,而是对其背后区域的内容应用滤镜。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>测试</title>
<style>
.glass-container {
display: grid;
place-items: center;
margin: 40px auto;
height: 400px;
border-radius: 10px;
/* 1. 设置一张背景图 */
background-image: url(https://picsum.photos/seed/glass/600/400);
background-size: cover;
}

.glass-card {
/* 由于容器没有宽度,在子元素中设置宽度就相当于设置容器宽度 */
width: 300px;
height: 200px;
/* 2. 设置一个半透明的背景色 */
background-color: rgba(255, 255, 255, 0.2);
/* 3. 核心:对卡片背后的区域应用模糊滤镜 */
backdrop-filter: blur(10px);
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.3);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);

/* 其他样式 */
color: white;
display: grid;
place-items: center;
font-size: 24px;
text-shadow: 1px 1px 5px black;
}
</style>
</head>

<body>
<div class="glass-container">
<div class="glass-card">
Glassmorphism
</div>
</div>
</body>

</html>

第八章:动态 CSS:@规则与动画

摘要: 在本章中,我们将为静态的页面注入生命力,全面开启 CSS 动态效果的大门。我们将首先学习 CSS 中强大的“宏观指令”——@规则,理解它们如何从更高维度控制样式表的行为,特别是为动画服务的 @keyframes。随后,我们将系统性地学习从 transform (变形)、transition (过渡) 到 animation (动画) 的完整动态效果体系,最终让您有能力创造出流畅、优雅且富有表现力的网页动画。


在本章的学习地图中,我们将:

  1. 首先,学习 @规则,这是 CSS 中用于执行宏观指令或条件化操作的基础。
  2. 接着,掌握 transform (变形),学习如何对元素进行移动、旋转、缩放等几何操作。
  3. 然后,我们将学习 transition (过渡),让元素在不同状态间的变化过程变得如丝般顺滑。
  4. 最后,我们将把所有知识融会贯通,精通 animation (动画),编排复杂的多步动画序列。

8.1. CSS @规则:代码的宏观指令

@规则 (At-rules) 是一种特殊的 CSS 语句,以 @ 符号开头,用于下达一些比单个样式声明更宏观的指令,例如导入外部文件、定义动画关键帧、或根据条件应用样式等,注意,此小节仅作了解,使用次数并不多

8.1.1. 常规 @规则

这类规则通常是独立的指令,以分号 ; 结尾。

  • @charset: 定义 CSS 文件本身使用的字符编码。

    1
    2
    /* 必须是样式文件的第一行,且不能有任何前置字符 */
    @charset "UTF-8";
  • @import: 在一个 CSS 文件中导入另一个 CSS 文件的内容。

    1
    2
    /* 导入一个名为 theme.css 的样式表 */
    @import url("theme.css");
    深入探讨
    2025-08-26

    @import<link> 标签都能引入 CSS,它们有什么区别吗?

    P
    Prorise

    问得好!这是个非常重要的性能问题。<link> 是 HTML 标签,它会在页面加载时并行下载 CSS 文件,不阻塞浏览器渲染。而 @import 是 CSS 规则,它会等到包含它的 CSS 文件被下载并解析后,才串行地去下载被导入的文件。

    P
    Prorise

    这会导致页面渲染被延迟,出现“白屏”时间更长的问题。因此,在原生前端中,我们强烈推荐始终使用 <link> 标签来引入所有 CSS 文件,而在脚手架环境下,也就是后续我们要学习的 Vue / React 是为了方便管理样式文件,且现代构建工具会处理好它的加载问题,所以对于这个规则,反而我们需要了解

  • @namespace: 用于在混合 XML 的文档(如内联 SVG)中定义命名空间,以避免选择器冲突。这是一个较少见的用法,了解即可。

8.1.2. 嵌套 @规则

这类规则包含一个规则块 {},可以在其中嵌套其他的 CSS 样式。

  • @media: 媒体查询,我们在第六章响应式设计中已经深入学习过。它根据设备特性(如视口宽度)来条件性地应用样式。
  • @supports: 功能查询,用于检查浏览器是否支持某个特定的 CSS 属性或值,从而实现优雅降级。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <style>
    .container {
    /* 默认使用 flex 布局作为备选方案 */
    display: flex;
    justify-content: center;
    }

    /* 如果浏览器支持 grid 布局,则使用更优的 grid 方案 */
    @supports (display: grid) {
    .container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    }
    }
    </style>
  • @font-face: 允许我们引入并使用自定义的字体文件。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @font-face {
    font-family: "MyCustomFont"; /* 为字体命名 */
    src: url("/fonts/my-font.woff2") format("woff2"); /* 指定字体文件路径 */
    font-weight: 400;
    font-style: normal;
    }
    body {
    font-family: "MyCustomFont", sans-serif; /* 在页面中使用自定义字体 */
    }
  • @keyframes: (本章重点) 用于定义一个动画的完整序列。我们将在 8.4 节详细讲解。

8.2. CSS 变形 (transform):元素的几何魔法

transform 属性允许我们在不影响元素在文档流中所占空间的情况下,对其进行移动、旋转、缩放和倾斜等视觉变换。

8.2.1. 2D 变形函数

函数核心描述示例
translate()平移。沿 X 轴和 Y 轴移动元素。transform: translate(50px, 20px);
scale()缩放。放大或缩小元素。transform: scale(1.2); (放大 20%)
rotate()旋转。顺时针旋转元素。transform: rotate(45deg);
skew()倾斜。沿 X 轴和 Y 轴倾斜元素。transform: skew(10deg, 5deg);

transform 属性可以接受多个变形函数,用空格隔开,它们会按照从左到右的顺序依次生效。

8.2.2. 改变变形原点 (transform-origin)

默认情况下,所有的变形(如旋转、缩放)都是以元素的中心点 (50% 50%) 为基准的。transform-origin 属性可以改变这个基准点。

实战: 创建多样化的悬停变形效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<style>
.transform-gallery { display: flex; justify-content: space-around; padding: 40px 0; }
.t-box {
width: 100px;
height: 100px;
background: #8e44ad;
color: white;
display: grid;
place-items: center;
/* 我们将在 8.3 节为它加上 transition 来变得平滑 */
}

.t-translate:hover {
transform: translateY(-20px); /* 向上平移 */
}
.t-scale:hover {
transform: scale(1.2); /* 放大 */
}
.t-rotate:hover {
transform: rotate(15deg); /* 旋转 */
}
.t-skew:hover {
transform: skew(-15deg); /* 倾斜 */
}
.t-origin:hover {
transform-origin: top left; /* 将变形原点设为左上角 */
transform: rotate(15deg);
}
</style>
<div class="transform-gallery">
<div class="t-box t-translate">Translate</div>
<div class="t-box t-scale">Scale</div>
<div class="t-box t-rotate">Rotate</div>
<div class="t-box t-skew">Skew</div>
<div class="t-box t-origin">Origin</div>
</div>

8.3. CSS 过渡 (transition):让变化如丝般顺滑

transition 属性可以让元素的样式变化不再是瞬间完成的,而是以一个平滑的、有持续时间的动画过程来呈现。

8.3.1. 过渡核心属性

属性核心描述示例
transition-property指定对哪个 CSS 属性应用过渡效果。all 表示所有可过渡的属性。transition-property: background-color;
transition-duration指定过渡效果完成所需的时间。transition-duration: 0.3s;
transition-timing-function指定过渡的速度曲线(动画节奏)。transition-timing-function: ease-in-out;
transition-delay指定过渡效果开始前的延迟时间。transition-delay: 0.1s;

深入讲解 transition-timing-function (速度曲线)

速度曲线决定了动画的“性格”。

  • ease: 默认值,慢速开始 -> 加速 -> 慢速结束。
  • linear: 匀速。
  • ease-in: 慢速开始。
  • ease-out: 慢速结束。
  • ease-in-out: 慢速开始和结束。
  • cubic-bezier(...): 允许你通过贝塞尔曲线创建完全自定义的速度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS过渡速度曲线演示</title>
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
}

.container {
display: flex;
flex-direction: column;
gap: 20px;
}

.demo-item {
display: flex;
align-items: center;
gap: 20px;
}

.box {
width: 50px;
height: 50px;
background-color: #3498db;
border-radius: 8px;
cursor: pointer;
transition-property: transform;
transition-duration: 2s;
animation: moveBox 2s infinite alternate;
}

@keyframes moveBox {
from {
transform: translateX(0);
}
to {
transform: translateX(200px);
}
}
/* 这里为了看出效果,我们提前使用一下animation */

.ease { animation-timing-function: ease; }
.linear { animation-timing-function: linear; }
.ease-in { animation-timing-function: ease-in; }
.ease-out { animation-timing-function: ease-out; }
.ease-in-out { animation-timing-function: ease-in-out; }
.cubic-bezier { animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); }

.label {
width: 150px;
font-size: 14px;
color: #333;
}
</style>
</head>
<body>
<div class="container">
<h1>CSS 过渡速度曲线演示</h1>
<p>方块会持续运动展示不同的过渡效果</p>

<div class="demo-item">
<div class="box ease"></div>
<div class="label">ease (默认)</div>
</div>

<div class="demo-item">
<div class="box linear"></div>
<div class="label">linear (匀速)</div>
</div>

<div class="demo-item">
<div class="box ease-in"></div>
<div class="label">ease-in (慢速开始)</div>
</div>

<div class="demo-item">
<div class="box ease-out"></div>
<div class="label">ease-out (慢速结束)</div>
</div>

<div class="demo-item">
<div class="box ease-in-out"></div>
<div class="label">ease-in-out (慢速开始和结束)</div>
</div>

<div class="demo-item">
<div class="box cubic-bezier"></div>
<div class="label">cubic-bezier (自定义)</div>
</div>
</div>
</body>
</html>

8.3.2. 简写与应用

通常,我们使用 transition 简写属性来定义过渡。
语法: transition: <property> <duration> <timing-function> <delay>;

实战:8.2 节的变形效果添加平滑过渡。这是 transition 最经典的用途——与 :hover 状态和 transform 属性结合,创造平滑的交互动画。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style>
.transition-gallery { display: flex; justify-content: space-around; padding: 40px 0; }
.smooth-box {
width: 100px;
height: 100px;
background: #27ae60;
color: white;
display: grid;
place-items: center;
/* 核心:添加 transition 属性 */
transition: transform 0.4s ease-in-out, background-color 0.4s ease;
}
.smooth-box:hover {
background-color: #2ecc71;
transform: translateY(-20px) scale(1.1);
}
</style>
<div class="transition-gallery">
<div class="smooth-box">Hover Me</div>
<div class="smooth-box">Hover Me</div>
<div class="smooth-box">Hover Me</div>
</div>

最佳实践: 将 transition 属性定义在元素的 默认状态 下,而不是 :hover 状态下。这样可以确保无论是鼠标移入还是移出,过渡效果都能平滑地触发。


8.4. CSS 动画 (animation):编排你的动画电影

transition 只能处理从 A 状态到 B 状态的简单过渡,而 animation 属性则要强大得多。它允许我们与 @keyframes 规则配合,创建包含多个中间步骤、可以无限循环、并能控制播放方向的复杂动画序列。

重要信息: 我们后续会使用 GSAP 这个超强的动画库来协助我们的动画效果,所以对于动画的理解,我建议您仅理解到 transform 以及 transition 即可,当然学到多也没有什么不好的!

8.4.1. 定义关键帧 (@keyframes)

@keyframes 规则是动画的“剧本”或“故事板”。我们在这里定义动画过程中的各个关键节点(关键帧)以及在这些节点上元素的样式。

语法:

  • 使用 from (等同于 0%) 和 to (等同于 100%) 定义动画的开始和结束。
  • 使用百分比 (0%100%) 来定义动画过程中的任意中间状态。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/* 定义一个名为 "slide-in" 的动画 */
@keyframes slide-in {
from {
/* 动画开始时,元素在左侧屏幕外且完全透明 */
transform: translateX(-100%);
opacity: 0;
}

to {
/* 动画结束时,元素回到原位且完全不透明 */
transform: translateX(0);
opacity: 1;
}
}

/* 定义一个更复杂的 "pulse" 动画 */
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
background-color: #e74c3c;
}
100% {
transform: scale(1);
}
}

8.4.2. 动画核心属性详解

定义好 @keyframes 剧本后,我们需要在元素上使用 animation 相关属性来“播放”这个剧本。

属性核心描述
animation-name指定要绑定的 @keyframes 动画的名称。
animation-duration指定动画完成一个周期的时长。
animation-timing-function速度曲线,用法与 transition 完全相同。
animation-delay指定动画开始播放前的延迟时间。
animation-iteration-count指定动画的播放次数,infinite 表示无限循环。
animation-direction指定动画在一个周期结束后是否反向播放,alternate 表示交替反向。
animation-fill-mode定义动画在播放前后(非播放状态时)的样式。forwards 表示停留在最后一帧。
animation-play-state控制动画的播放状态,paused (暂停) 或 running (运行)。

8.4.3. animation (简写属性)

transition 类似,我们通常使用 animation 简写属性来一次性设置所有动画参数。
语法: animation: <name> <duration> <timing-function> <delay> <iteration-count> <direction> <fill-mode>;

在简写中,至少需要指定 animation-nameanimation-duration 才能让动画生效。

8.4.4. 实战:创建一个无限循环的呼吸灯加载动画

任务: 创建一个由小到大、由暗到亮并无限循环的“呼吸”效果,这是加载动画中最常见的类型之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<style>
.loader-container {
display: grid;
place-items: center;
height: 200px;
}

/* 1. 定义呼吸动画的关键帧 */
@keyframes breathe {
0% {
transform: scale(0.8);
opacity: 0.5;
}
50% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(0.8);
opacity: 0.5;
}
}

.breathing-dot {
width: 50px;
height: 50px;
background-color: #3498db;
border-radius: 50%;

/* 2. 应用动画 */
animation-name: breathe; /* 动画名称 */
animation-duration: 2s; /* 持续时间 */
animation-timing-function: ease-in-out; /* 速度曲线 */
animation-iteration-count: infinite; /* 无限循环 */
}
</style>

<div class="loader-container">
<div class="breathing-dot"></div>
</div>

8.5. 本章核心速查总结与疑难解答

速查表

分类关键项核心描述
@规则@import vs <link>(推荐) 始终使用 <link>,因为它并行加载,性能更优。
@supportsCSS 功能查询,用于实现优雅降级。
@keyframes(核心) 定义动画序列的“故事板”。
变形transformtranslate (平移), scale (缩放), rotate (旋转), skew (倾斜)。不影响文档流。
transform-origin改变变形的中心点。
过渡transition(核心) 使元素在不同状态间的样式变化变得平滑。应定义在默认状态下。
动画animation(核心) 结合 @keyframes 创建复杂、可循环的多步动画。

第九章:CSS 工程化:变量、命名与代码架构

摘要: 在本章中,我们将跳出单个 CSS 属性的学习,从更高维度审视如何构建一个健壮、可维护的 CSS 项目。我们将深入两大现代 CSS 工程化基石:CSS 变量 (Custom Properties),学习如何创建可复用的设计规范和实现一键换肤;以及 BEM 命名规范,掌握一套业界公认的、能从根本上避免样式冲突的代码组织方法论。


在本章的学习地图中,我们将:

  1. 首先,直面传统 CSS 开发中的“魔术数字”与样式重复的痛点。
  2. 接着,我们将学习 CSS 变量,掌握在原生 CSS 中实现动态主题化的强大能力。
  3. 然后,我们将深入 BEM 命名规范,学习如何构建清晰、无冲突、可扩展的组件化 CSS。
  4. 最后,我们将把这两种工程化思想结合起来,进行总结和答疑。

9.1. CSS 变量:开启主题化的大门

CSS 变量(官方称为“自定义属性”)是现代 CSS 中一项革命性的功能,它允许我们将一个值存储在一个变量中,并在样式表的任何地方重复使用。

9.1.1. 痛点解析:“魔术数字”与重复的诅咒

痛点背景:在传统的 CSS 项目中,我们经常会遇到这样的代码:

1
2
3
4
5
6
7
8
9
10
11
.button-primary {
background-color: #3498db;
border: 1px solid #3498db;
}
.link-primary {
color: #3498db;
}
.card-header {
border-left: 5px solid #3498db;
}
/* ... 还有 20 处用到了这个蓝色 ... */

这里的 #3498db 就是一个“魔术数字”。想象一下,如果现在需要将品牌主色调从蓝色换成绿色,你将不得不进行 20 多次“查找与替换”操作,这既繁琐又极易出错。

9.1.2. 核心语法:声明、使用与作用域

CSS 变量完美地解决了这个问题。

  • 声明 (Declaration): 我们通常在 :root 伪类中声明全局变量,它代表了文档的根元素 (<html>)。变量名必须以两个连字符 -- 开头。
    1
    2
    3
    4
    5
    :root {
    --primary-color: #3498db;
    --base-font-size: 16px;
    --card-border-radius: 8px;
    }
  • 使用 (Usage): 通过 var() 函数来读取变量的值。var() 函数还可以接受第二个参数,作为备用值(当变量未定义时)。
    1
    2
    3
    4
    5
    6
    7
    .button-primary {
    /* 读取 --primary-color 变量 */
    background-color: var(--primary-color);

    /* 如果 --spacing-unit 未定义,则使用 10px 作为备用值 */
    padding: var(--spacing-unit, 10px);
    }
  • 作用域 (Scoping): CSS 变量遵循正常的 CSS 级联规则。你可以在更具体的选择器中重新定义变量,它会覆盖全局定义。这正是实现主题化的关键。

9.1.3. 实战:一键切换“暗黑模式”

任务: 创建一个简单的卡片组件,并通过切换 <html> 标签上的一个 data-theme 属性,实现白天/黑夜主题的瞬间切换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Theme Switcher</title>
<style>
/* 1. 在 :root 中定义默认的(亮色)主题变量 */
:root {
/* 定义变量为白色 */
--bg-color: #f0f2f5;
--card-bg: #ffffff;
--text-color: #333333;
--primary-color: #3498db;
}

/* 2. 当 html 标签有 data-theme="dark" 属性时,重定义这些变量 */
html[data-theme="dark"] {
/* 吧变量定义为黑色 */
--bg-color: #1a1a1a;
--card-bg: #2b2b2b;
--text-color: #e0e0e0;
--primary-color: #5dade2;
}

/* 3. 在 body 和 card 组件中,只使用变量 */
.theme-body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: sans-serif;
transition: background-color 0.3s;
min-height: 100vh;
margin: 0;
padding: 20px;
}

.theme-card {
background-color: var(--card-bg);
border-radius: 8px;
padding: 20px;
margin: 20px 0;
width: 250px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s, box-shadow 0.3s;
}

.theme-card h3 {
color: var(--primary-color);
}
</style>
</head>

<body class="theme-body">
<button onclick="toggleTheme()">切换主题</button>

<div>
<strong>当前主题:</strong>
<div class="theme-card">
<h3>卡片标题</h3>
<p>这是使用 CSS 变量的卡片内容。点击上方按钮可以切换亮色和暗色主题。</p>
</div>
</div>
<script>
// 写一些简单的Js代码实现切换
const toggleTheme = () => {
// 获取html标签
const html = document.documentElement;
// 获取html标签的data-theme属性
const currentTheme = html.getAttribute('data-theme');
// 如果当前主题为dark,则切换为light,否则切换为dark
if (currentTheme === 'dark') {
html.removeAttribute('data-theme'); // 移除dark属性
} else {
html.setAttribute('data-theme', 'dark'); // 添加dark属性
}
}
</script>

</body>

</html>

9.2. BEM 命名规范:告别样式冲突

BEM 是一种强大的 CSS 命名约定,旨在帮助开发者在大型项目中创建可复用、可维护的组件,从根本上避免样式冲突。

9.2.1. 痛点解析:全局作用域与“样式战争”

痛点背景: 考虑以下场景,你有两个独立的组件,它们都用到了 .title 这个通用的类名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<style>
/* 文章组件的标题样式 */
.article .title {
font-size: 24px;
color: black;
border-bottom: 2px solid #eee;
}

/* 商品卡片组件的标题样式 (后写的) */
.product .title {
font-size: 16px;
color: #555;
font-weight: normal;
}
</style>
<div class="article">
<h2 class="title">文章标题</h2>
<p>文章内容...</p>
</div>

<div class="product">
<h3 class="title">商品标题</h3>
<p>商品价格...</p>
</div>

如果某天,另一个开发者为了修复商品卡片的样式,不小心写了一个全局的 .title { color: red; },那么文章的标题也会被污染。为了覆盖这个样式,你可能需要写 .page .article > h2.title 这样更复杂的选择器,这就是所谓的“权重战争”,最终会让 CSS 变得一团糟。

9.2.2. BEM 方法论:块、元素与修饰符

BEM 通过一套简单的命名规则解决了这个问题,它将界面划分为三个部分:

概念语法作用示例
Block.block页面上一个独立的、可复用的组件。.user-profile
Element.block__elementBlock 的一个组成部分,不能脱离 Block 单独存在。.user-profile__avatar
Modifier.block--modifierBlock 或 Element 的一个变体或状态。.user-profile--vip, .user-profile__avatar--small

9.2.3. 实战:使用 BEM 重构一个卡片组件

任务: 将一个拥有混乱类名的卡片组件,用 BEM 规范进行重构。

重构前 (Before):

1
2
3
4
5
6
7
<div class="card featured">
<h2 class="title">卡片标题</h2>
<p class="text">卡片内容...</p>
<button class="btn primary">按钮</button>
</div>

<style> .title { ... } .text { ... } .btn { ... } .primary { ... } </style>

重构后 (After):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Theme Switcher</title>
<style>
/* BEM 规范的 CSS */
.bem-card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 20px;
}

/* 修饰符 --featured */
.bem-card--featured {
border-color: #3498db;
border-width: 2px;
}

/* 元素 __title */
.bem-card__title {
font-size: 20px;
margin-top: 0;
}

/* 元素 __text */
.bem-card__text {
margin-bottom: 15px;
}

/* 元素 __button */
.bem-card__button {
padding: 10px 15px;
border: none;
background-color: #ccc;
cursor: pointer;
}

/* 按钮的修饰符 --primary */
.bem-card__button--primary {
background-color: #3498db;
color: white;
}


.bem-card__button--primary:hover {
background-color: #2980b9;
}
</style>
</head>
<div class="bem-card bem-card--featured">
<h2 class="bem-card__title">卡片标题</h2>
<p class="bem-card__text">卡片内容...</p>
<button class="bem-card__button bem-card__button--primary">按钮</button>
</div>

</body>

</html>

BEM 的优势:

  • 无全局冲突: 所有样式都通过唯一的块(如 .bem-card)来限定作用域。
  • 低权重: 选择器都是单一的类,避免了复杂的权重计算和覆盖问题。
  • 自描述: 从类名 bem-card__title 就能清晰地看出它的结构和作用。

9.3. 本章核心速查总结与疑难解答

速查表

分类语法示例
CSS 变量声明--name: value;:root { --main-color: blue; }
CSS 变量使用var(--name, [fallback])color: var(--main-color, black);
BEM Block.block.nav-menu
BEM Element.block__element.nav-menu__item
BEM Modifier.block--modifier.nav-menu--dark

疑难解答

学习问答
2025-08-26

CSS 变量和 Sass/SCSS 中的变量有什么本质不同?

P
Prorise

这是一个非常核心的问题!最大的区别在于作用时机。Sass 变量(如 $color)是编译时的,当 Sass 代码被编译成 CSS 后,变量就不存在了,它只是一个方便我们开发的工具。而 CSS 变量 (--color) 是运行时的,它在浏览器中真实存在,你可以用 JavaScript 动态地去读取和修改它,这就是我们能实现“一键换肤”的根本原因。

BEM 命名看起来很长很丑,为什么它反而是业界推荐的规范?

P
Prorise

问得好!BEM 确实牺牲了一部分简洁性,但它换来的是大型项目中至关重要的三大特性:可读性、可预测性和零冲突。一个又长又明确的类名 (.user-profile__avatar--small) 远比一个简短但模糊的类名 (.avatar.small) 要好维护得多。在团队协作中,这种“丑陋”的明确性,远比个人审美上的“简洁”要宝贵。