主题
第 02 章 · CSS 基础
一句话开篇:CSS 是网页的「装修队」——HTML 立好了毛坯房,CSS 决定刷什么颜色、铺什么地砖、家具怎么摆。
0. 生活类比(先建立直觉)
把 CSS 想象成给房子做装修:
| 场景 | HTML | CSS |
|---|---|---|
| 立钢筋墙 | 写 <div class="room"> | — |
| 选墙漆颜色 | — | background-color: #fff; |
| 决定客厅 / 卧室大小 | — | width: 300px; height: 200px; |
| 沙发摆中间还是靠墙 | — | position、flex、grid |
| 挂画 hover 时变亮 | — | :hover、transition |
| 大屏幕和手机看到不一样的家 | — | 媒体查询(@media) |
CSS 的全称是 Cascading Style Sheets(层叠样式表):
- 层叠(Cascade):多条规则同时作用同一个元素时,按"权重 + 顺序"决定谁说了算(就像家里 3 个长辈意见不一致,要按辈分定)。
- 样式(Style):管颜色、大小、位置、动画。
- 样式表(Sheet):一张写满规则的"装修清单"。
学 CSS 最重要的是建立空间感:每个元素都是一个长方形盒子,CSS 就是给这些盒子排队、上色、摆姿势。
1. 概念是什么
1.1 写 CSS 的 3 种方式
| 方式 | 写法 | 适用 | 优先级 |
|---|---|---|---|
| 行内样式 | <div style="color:red"> | 临时调试 | 高 |
| 内部样式 | <style>...</style> 写在 head | 单页 demo | 中 |
| 外部样式 | <link rel="stylesheet" href="x.css"> | 实际项目 | 中 |
1.2 一条规则的解剖
选择器 {
属性: 值;
属性: 值;
}css
h1 { /* 选择器 */
color: red; /* 声明:属性: 值 */
font-size: 24px;
}一行 CSS = 给"哪些元素"应用"什么样式"。
2. 为什么需要它
只有 HTML 的网页:黑底白字、上下排列、所有按钮长得一样、PC 和手机看到完全相同的丑样。
CSS 解决了 4 件事:
- 结构与样式分离:HTML 专心描述"是什么",CSS 专心描述"长什么样"。
- 复用:一处改色,全站换肤。
- 响应式:一份代码,PC / 平板 / 手机自适应。
- 动效:让按钮"按下去"有反馈,让弹窗"飘进来",提升用户体验。
没有 CSS 的网页就像没装修的毛坯房——能住,但你不想住。
3. 怎么用(最小示例)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<style>
body { background: #f9fafb; font-family: sans-serif; }
h1 { color: #2563eb; text-align: center; }
.card { background: #fff; padding: 16px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,.1); }
</style>
</head>
<body>
<h1>你好,CSS</h1>
<div class="card">这是一张卡片</div>
</body>
</html>4. 进阶要点
4.1 选择器(重头戏)
| 类型 | 写法 | 含义 | 权重 |
|---|---|---|---|
| 通配符 | * | 所有元素 | 0,0,0,0 |
| 标签 | h1, p | 所有该标签 | 0,0,0,1 |
| 类 | .btn | 所有 class 含 btn 的元素 | 0,0,1,0 |
| ID | #header | id="header" 的元素(唯一) | 0,1,0,0 |
| 属性 | input[type=text] | type 等于 text 的 input | 0,0,1,0 |
| 伪类 | a:hover, li:first-child | 元素的特定状态 | 0,0,1,0 |
| 伪元素 | p::before, p::after | 虚拟生成的子元素 | 0,0,0,1 |
| 行内 style | style="..." | 直接写在标签上 | 1,0,0,0 |
| !important | color: red !important | 强制最高 | ∞ |
组合选择器:
css
/* 后代(空格):article 内的所有 p(包括嵌套深层) */
article p { color: gray; }
/* 子选择器(>):article 的直接子级 p */
article > p { font-weight: bold; }
/* 相邻兄弟(+):紧跟 h2 的下一个 p */
h2 + p { margin-top: 0; }
/* 通用兄弟(~):h2 后所有同级 p */
h2 ~ p { color: green; }
/* 群组(,):h1 和 h2 都用这个样式 */
h1, h2 { font-family: serif; }
/* 同时满足:class 是 btn 且是 a 标签 */
a.btn { padding: 8px 16px; }常用伪类:
css
a:link { color: blue; } /* 未访问 */
a:visited { color: purple; } /* 已访问 */
a:hover { color: red; } /* 鼠标悬停 */
a:active { color: orange; } /* 按下瞬间 */
input:focus { border-color: blue; }
input:disabled { background: #eee; }
input:checked { /* checkbox / radio 选中 */ }
li:first-child { color: red; }
li:last-child { color: blue; }
li:nth-child(2) { color: green; }
li:nth-child(odd) { background: #f9f9f9; } /* 奇数 */
li:nth-child(even) { background: #fff; } /* 偶数 */
p:not(.special) { color: black; } /* 不是 .special 的 p */伪元素(注意是双冒号 ::):
css
p::before { content: "👉 "; } /* 在 p 内容前插入 */
p::after { content: " 完"; } /* 在 p 内容后插入 */
p::first-letter { font-size: 2em; } /* 首字下沉 */
p::first-line { font-weight: bold; } /* 第一行加粗 */
::selection { background: yellow; } /* 鼠标选中文本时 */4.2 盒模型(最最最重要)
每个元素都是一个矩形盒子,从外到内 4 层:
┌──────────── margin(外边距,与别人保持距离) ────────────┐
│ ┌────────── border(边框) ──────────────────────┐ │
│ │ ┌──────── padding(内边距,内容到边的距离) ───┐ │ │
│ │ │ ┌──────────────────────────────────┐ │ │ │
│ │ │ │ content(内容) │ │ │ │
│ │ │ │ width × height │ │ │ │
│ │ │ └──────────────────────────────────┘ │ │ │
│ │ └──────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘两种盒模型:
box-sizing 值 | width 含义 | 实际占位 |
|---|---|---|
content-box(默认) | 只算 content | width + padding + border |
border-box | content + padding + border | 就是 width |
css
/* 现代项目几乎都加这一行:让 width 包含 padding 和 border */
* { box-sizing: border-box; }margin 折叠(坑):
两个垂直方向相邻的元素,margin 不会叠加,而是取最大值。
css
.a { margin-bottom: 30px; }
.b { margin-top: 20px; }
/* a 和 b 之间的距离不是 50px,而是 30px! */4.3 定位(position)
| 值 | 含义 | 类比 |
|---|---|---|
static(默认) | 跟随文档流 | 排队站好 |
relative | 相对自己原来位置偏移 | 站着不动,但叫人"看我从右移 10cm" |
absolute | 相对最近的"非 static 父元素"定位 | 像便利贴粘到墙上 |
fixed | 相对浏览器窗口固定 | 像贴在车窗上的吸盘玩偶,车开它不动 |
sticky | 滚动到某位置后吸顶 | 表头吸顶 |
css
.parent { position: relative; } /* 给绝对定位的子元素当"地基" */
.child { position: absolute; top: 0; right: 0; } /* 钉在父元素右上角 */
.fixed-btn { position: fixed; bottom: 20px; right: 20px; } /* 始终右下角 */
.sticky-h { position: sticky; top: 0; } /* 滚到顶就吸住 */z-index:层级(数字越大越在上面)。只对非 static 元素生效。
4.4 Flexbox(一维布局神器)
把容器变成 flex container,子元素自动按"一行"或"一列"排列。
父元素(display: flex)
┌───────────────────────────────────────────┐
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │ → 主轴方向
│ │子1 │ │子2 │ │子3 │ │子4 │ │
│ └────┘ └────┘ └────┘ └────┘ │
└───────────────────────────────────────────┘容器属性:
| 属性 | 值 | 含义 |
|---|---|---|
display | flex | 启用 flex |
flex-direction | row / row-reverse / column / column-reverse | 主轴方向 |
justify-content | flex-start / center / flex-end / space-between / space-around / space-evenly | 主轴对齐 |
align-items | stretch(默认) / center / flex-start / flex-end / baseline | 交叉轴对齐 |
flex-wrap | nowrap(默认) / wrap | 换不换行 |
gap | 数值 | 子元素间距 |
子元素属性:
| 属性 | 含义 |
|---|---|
flex-grow | 剩余空间分配比例(默认 0,不占) |
flex-shrink | 空间不够时缩小比例(默认 1) |
flex-basis | 初始宽度 |
flex | 简写,常用 flex: 1(= grow:1 shrink:1 basis:0) |
align-self | 单独覆盖父级的 align-items |
order | 排序(默认 0,越小越靠前) |
经典需求:
css
/* 水平垂直居中(曾经是面试重灾区,现在 3 行搞定) */
.center {
display: flex;
justify-content: center;
align-items: center;
}
/* 左右两端布局 */
.nav {
display: flex;
justify-content: space-between;
}
/* 三栏,中间自适应 */
.layout > .side { width: 200px; }
.layout > .main { flex: 1; }4.5 Grid(二维布局神器)
Flex 是"一行排队",Grid 是"棋盘式"——同时控制行和列。
css
.container {
display: grid;
grid-template-columns: 200px 1fr 1fr; /* 3 列:200px、1份、1份 */
grid-template-rows: 80px auto 60px; /* 3 行 */
gap: 12px; /* 行列间距 */
}fr 单位:剩余空间的"份数"。1fr 2fr = 1:2 分配剩余。
响应式网格(一行搞定卡片墙自适应):
css
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
}含义:自动填充列,每列最小 200px,最大 1fr。窗口变窄时自动减少列数。
命名区域(最直观的写法):
css
.layout {
display: grid;
grid-template-areas:
"header header"
"side main"
"footer footer";
grid-template-columns: 200px 1fr;
}
.layout > header { grid-area: header; }
.layout > .side { grid-area: side; }
.layout > .main { grid-area: main; }
.layout > footer { grid-area: footer; }4.6 Flex vs Grid 对比
| 维度 | Flex | Grid |
|---|---|---|
| 维度 | 一维(行 或 列) | 二维(行 和 列) |
| 适用 | 导航栏、按钮组、单列列表 | 整页布局、卡片墙、复杂表格 |
| 关注点 | 子元素如何排队 | 容器如何分格 |
| 学习曲线 | 简单 | 中等 |
| 兼容性 | 极好 | 现代浏览器都支持 |
经验法则:整页布局用 Grid,组件内部用 Flex。
4.7 响应式(媒体查询)
css
/* 默认:手机优先样式 */
.container { width: 100%; }
/* 平板(≥768px) */
@media (min-width: 768px) {
.container { width: 720px; margin: 0 auto; }
}
/* PC(≥1024px) */
@media (min-width: 1024px) {
.container { width: 960px; }
}常用断点(参考 Bootstrap 5):
| 设备 | 断点 |
|---|---|
| 超小(手机) | < 576px |
| 小(大手机) | ≥ 576px |
| 中(平板) | ≥ 768px |
| 大(桌面) | ≥ 992px |
| 超大(大桌面) | ≥ 1200px |
| 超超大 | ≥ 1400px |
移动优先 vs 桌面优先:
- 移动优先:默认写手机样式,用
min-width加大屏样式(推荐)。 - 桌面优先:默认写桌面样式,用
max-width加小屏样式。
4.8 单位
| 单位 | 含义 | 适用 |
|---|---|---|
px | 像素(绝对) | 边框、阴影 |
% | 父元素的百分比 | 弹性宽度 |
em | 当前字号的倍数 | 间距 |
rem | 根字号(html 的 font-size)的倍数 | 全站缩放 |
vw / vh | 视口宽 / 高的 1% | 全屏布局 |
fr | grid 中的"份" | grid 列宽 |
rem 全站缩放套路:
css
html { font-size: 16px; }
body { font-size: 1rem; }
h1 { font-size: 2rem; } /* 32px */
.card { padding: 1.5rem; } /* 24px */
/* 改一处,全站等比例缩放 */
@media (max-width: 480px) { html { font-size: 14px; } }4.9 动画
transition(过渡):从 A 状态平滑变到 B 状态。
css
.btn {
background: #2563eb;
transition: background 0.3s ease, transform 0.2s;
}
.btn:hover {
background: #1d4ed8;
transform: translateY(-2px);
}transition 4 件套:property duration timing-function delay。
transform 常用变换:
| 函数 | 作用 |
|---|---|
translate(x, y) | 平移 |
rotate(45deg) | 旋转 |
scale(1.2) | 缩放 |
skew(10deg) | 倾斜 |
animation(关键帧动画):可循环、可暂停。
css
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-8px); }
75% { transform: translateX(8px); }
}
.error {
animation: shake 0.4s;
}timing-function:ease(默认)/ linear / ease-in / ease-out / ease-in-out / cubic-bezier(...)。
4.10 颜色与背景
css
color: red; /* 关键字 */
color: #ff0000; /* 16 进制 */
color: rgb(255, 0, 0);
color: rgba(255, 0, 0, 0.5); /* 带透明度 */
color: hsl(0, 100%, 50%); /* 色相、饱和度、亮度 */
background-color: #f9fafb;
background-image: url('bg.jpg');
background: linear-gradient(135deg, #6366f1, #8b5cf6); /* 渐变 */
background: radial-gradient(circle, #fff, #ddd);
box-shadow: 0 2px 8px rgba(0,0,0,.1); /* 阴影 */
border-radius: 8px; /* 圆角 */5. 实战案例
详见 examples/ 目录:
box-model.html— 盒模型可视化flexbox.html— Flex 各种对齐演示grid.html— Grid 经典布局animation.html— 过渡 + 关键帧动画responsive.html— 媒体查询响应式
完整 demo 见 demo/index.html:响应式商品卡片墙。
6. ⚠️ 易踩的坑
坑 1:忘了 box-sizing: border-box
默认 width 不含 padding 和 border,导致"明明设了 200px,加 padding 后变 220px"——布局算不准。新项目第一行写:
css
*, *::before, *::after { box-sizing: border-box; }坑 2:margin 上下折叠
html
<div style="margin-bottom: 30px">A</div>
<div style="margin-top: 20px">B</div>A 和 B 之间不是 50px,是 30px(取最大值)。解决:用 padding,或加 border、overflow: hidden 等触发 BFC。
坑 3:absolute 找不到正确的"地基"
绝对定位元素是相对最近的非 static 父元素定位。如果父元素都没设 position,会一路找到 body。
口诀:子绝对,父相对(.parent { position: relative; })。
坑 4:z-index 不生效
z-index 只对非 static 元素生效。常忘记给 .modal 加 position: fixed/absolute。
坑 5:!important 滥用
新手遇到样式不生效就加 !important,最后一堆 !important 互相打架。先想优先级,再想权重,最后才用 important。
坑 6:选择器权重计算错误
css
#a .b p { color: red; } /* 0,1,1,1 */
.x .y .z p { color: blue; } /* 0,0,3,1 */
/* id > 多个 class,红色赢 */记住:(行内, id, class/伪类/属性, 标签/伪元素)。
坑 7:行内元素设宽高无效
css
span { width: 100px; } /* ❌ 无效 */
span { display: inline-block; width: 100px; } /* ✅ */坑 8:图片下方有 3px 缝隙
img 是行内元素,受 vertical-align: baseline 影响,底部有空隙。
css
img { display: block; } /* 或 vertical-align: middle */坑 9:忘了 viewport meta,写了响应式也没用
html
<meta name="viewport" content="width=device-width, initial-scale=1.0" />不写这一行,手机会按 980px 缩放,媒体查询永远命中桌面分支。
坑 10:用 background-color 给图标着色
background-color 只能改背景色,改不了 SVG/字体图标的颜色。SVG 用 fill,字体图标用 color。
坑 11:动画导致页面卡顿
动 width / height / top / left 会触发 重排(reflow),性能差。改用 transform(平移、缩放)和 opacity,由 GPU 加速。
css
/* ❌ 卡 */
.box { left: 0; transition: left 0.3s; }
.box:hover { left: 10px; }
/* ✅ 顺滑 */
.box { transform: translateX(0); transition: transform 0.3s; }
.box:hover { transform: translateX(10px); }坑 12:Flex 子元素 flex: 1 不等宽
flex: 1 等价于 flex: 1 1 0,从 0 开始按比例生长,等宽。 但如果用 flex: 1 1 auto,从内容宽度开始生长,内容多的更宽。
7. 一句话总结
CSS 是给 HTML 装修:选对盒模型、玩转 Flex 和 Grid、记住权重和层叠规则,再配合媒体查询和动画——你就能让网页在任何屏幕上都漂亮又流畅。