HTML 与 CSS 入门
相关文档:JavaScript入门(/javascript-ru-men/) Vue3入门(/vue-3-ru-men-zhi-nan/) 1. HTML 是什么(#html-%E6%98%AF%E4%BB%80%E4%B9%88) 2. 网页的基本结构(#%E7%BD%91%E9%A1%B5%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%BB%93%E6%9E%84) 3. 常用 HTML 标签(#%E5%B8%B8%E7%94%A8-html-%E6%A0%87%E7%AD%BE) 4. HTML 属性(#html-%E5%B1%9E%
官方文档:https://developer.mozilla.org/zh-CN/docs/Web/HTML | https://developer.mozilla.org/zh-CN/docs/Web/CSS
最后更新:2026-03-05
相关文档:JavaScript入门 Vue3入门
目录
- HTML 是什么
- 网页的基本结构
- 常用 HTML 标签
- HTML 属性
- 路径:相对路径与绝对路径
- 表单完整示例
- CSS 是什么
- CSS 三种引入方式
- CSS 选择器
- 盒模型
- 常用 CSS 属性
- display 属性
- Flexbox 弹性布局
- Grid 网格布局
- Position 定位
- 响应式设计
- 过渡与动画
- CSS 变量
- 综合实战:响应式个人主页
- 注意事项与常见错误
HTML 是什么
HTML(HyperText Markup Language,超文本标记语言)是构建网页的基础语言。它不是编程语言,而是一种标记语言,通过标签(Tag)来描述网页的内容结构。
你可以把网页理解为一栋房子:
- HTML 是房子的骨架和墙体(结构)
- CSS 是房子的装修和外观(样式)
- JavaScript 是房子里的电器和功能(交互)
本文只讲 HTML 和 CSS。
网页的基本结构
每个 HTML 文件都遵循固定的基本结构。用文本编辑器(如 VS Code)新建一个 index.html 文件,写入以下内容:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>我的第一个网页</title>
</head>
<body>
<h1>你好,世界!</h1>
<p>这是我的第一个网页。</p>
</body>
</html>
各部分说明:
| 标签 / 声明 | 作用 |
|---|---|
<!DOCTYPE html> |
声明文档类型为 HTML5,必须写在第一行 |
<html lang="zh-CN"> |
根标签,包裹整个页面;lang 属性声明页面语言 |
<head> |
头部区域,存放不显示在页面上的元信息 |
<meta charset="UTF-8"> |
声明字符编码为 UTF-8,防止中文乱码 |
<meta name="viewport" ...> |
适配移动端屏幕,响应式设计必备 |
<title> |
浏览器标签页上显示的标题 |
<body> |
页面可见内容都写在这里 |
双击 index.html 文件,浏览器会直接打开并渲染出网页。
常用 HTML 标签
标题标签 h1 - h6
HTML 提供六级标题,h1 最大,h6 最小。每个页面通常只有一个 h1。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>标题示例</title>
</head>
<body>
<h1>一级标题(最重要)</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h4>四级标题</h4>
<h5>五级标题</h5>
<h6>六级标题(最小)</h6>
</body>
</html>
段落标签 p
<p> 标签表示一段文字。浏览器会在段落前后自动添加间距。
<p>这是第一段文字。HTML 中的换行在源代码里不起作用,</p>
<p>浏览器会把多个空格和换行都压缩成一个空格。</p>
<!-- 如果要强制换行,使用 <br> 标签 -->
<p>第一行文字<br />第二行文字(br 是自闭合标签)</p>
链接标签 a
<a> 标签创建超链接,点击后跳转到指定地址。
<!-- 跳转到外部网站,target="_blank" 表示在新标签页打开 -->
<a href="https://www.baidu.com" target="_blank">打开百度</a>
<!-- 跳转到同一网站的另一个页面(相对路径) -->
<a href="about.html">关于我们</a>
<!-- 跳转到页面内的某个位置(锚点跳转) -->
<a href="#section-2">跳转到第二节</a>
<!-- 发送邮件 -->
<a href="mailto:[email protected]">联系我</a>
<!-- 拨打电话(手机端) -->
<a href="tel:+8613800138000">拨打电话</a>
图片标签 img
<img> 是自闭合标签,用于显示图片。
<!-- src 是图片地址,alt 是图片无法显示时的替代文字(必须写) -->
<img src="photo.jpg" alt="一张风景照" />
<!-- 设置宽高(推荐只设置其中一个,让另一个自动等比缩放) -->
<img src="logo.png" alt="网站标志" width="200" />
<!-- 使用网络图片 -->
<img src="https://via.placeholder.com/300x200" alt="占位图片" />
列表标签
无序列表(ul + li):列表项前显示圆点。
<ul>
<li>苹果</li>
<li>香蕉</li>
<li>橙子</li>
</ul>
有序列表(ol + li):列表项前显示数字。
<ol>
<li>第一步:打开冰箱</li>
<li>第二步:把大象放进去</li>
<li>第三步:关上冰箱</li>
</ol>
列表嵌套:
<ul>
<li>水果
<ul>
<li>苹果</li>
<li>香蕉</li>
</ul>
</li>
<li>蔬菜
<ul>
<li>白菜</li>
<li>萝卜</li>
</ul>
</li>
</ul>
表格标签
<table border="1">
<!-- 表格标题(可选) -->
<caption>学生成绩表</caption>
<!-- 表头区域 -->
<thead>
<tr>
<!-- th 是表头单元格,默认加粗居中 -->
<th>姓名</th>
<th>语文</th>
<th>数学</th>
<th>英语</th>
</tr>
</thead>
<!-- 表体区域 -->
<tbody>
<tr>
<!-- td 是普通单元格 -->
<td>张三</td>
<td>90</td>
<td>85</td>
<td>92</td>
</tr>
<tr>
<td>李四</td>
<td>78</td>
<td>95</td>
<td>88</td>
</tr>
</tbody>
<!-- 表脚区域(可选) -->
<tfoot>
<tr>
<td>平均分</td>
<td>84</td>
<td>90</td>
<td>90</td>
</tr>
</tfoot>
</table>
单元格合并:
<table border="1">
<tr>
<!-- colspan 跨列合并(横向合并) -->
<td colspan="2">合并了两列</td>
<td>正常单元格</td>
</tr>
<tr>
<!-- rowspan 跨行合并(纵向合并) -->
<td rowspan="2">合并了两行</td>
<td>第二行第二列</td>
<td>第二行第三列</td>
</tr>
<tr>
<td>第三行第二列</td>
<td>第三行第三列</td>
</tr>
</table>
表单标签
表单用于收集用户输入的数据。详细示例见表单完整示例。
<!-- action 是提交数据的地址,method 是提交方式 -->
<form action="/submit" method="post">
<!-- 单行文本输入 -->
<input type="text" name="username" placeholder="请输入用户名" />
<!-- 密码输入(内容显示为圆点) -->
<input type="password" name="password" placeholder="请输入密码" />
<!-- 邮箱输入(会验证格式) -->
<input type="email" name="email" placeholder="请输入邮箱" />
<!-- 数字输入 -->
<input type="number" name="age" min="1" max="120" />
<!-- 单选框(name 相同的单选框为一组,只能选一个) -->
<input type="radio" name="gender" value="male" /> 男
<input type="radio" name="gender" value="female" /> 女
<!-- 复选框 -->
<input type="checkbox" name="hobby" value="reading" /> 阅读
<input type="checkbox" name="hobby" value="coding" /> 编程
<!-- 下拉选择框 -->
<select name="city">
<option value="">请选择城市</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
</select>
<!-- 多行文本框 -->
<textarea name="message" rows="4" cols="40" placeholder="请输入留言"></textarea>
<!-- 提交按钮 -->
<button type="submit">提交</button>
<!-- 重置按钮(清空表单) -->
<button type="reset">重置</button>
</form>
语义化标签
语义化标签让代码的含义更清晰,对搜索引擎和屏幕阅读器(辅助视障用户)友好。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>语义化布局示例</title>
</head>
<body>
<!-- 页头,通常包含网站标志和导航 -->
<header>
<h1>网站名称</h1>
<!-- 导航栏 -->
<nav>
<ul>
<li><a href="/">首页</a></li>
<li><a href="/about">关于</a></li>
<li><a href="/contact">联系</a></li>
</ul>
</nav>
</header>
<!-- 页面主体内容,每个页面只有一个 main -->
<main>
<!-- 独立的内容区块 -->
<section>
<h2>最新文章</h2>
<!-- 一篇独立的文章或内容块 -->
<article>
<h3>文章标题一</h3>
<p>文章摘要内容……</p>
</article>
<article>
<h3>文章标题二</h3>
<p>文章摘要内容……</p>
</article>
</section>
<section>
<h2>关于我们</h2>
<p>这里是关于我们的介绍。</p>
</section>
</main>
<!-- 侧边栏 -->
<aside>
<h2>相关推荐</h2>
<ul>
<li><a href="#">推荐文章一</a></li>
<li><a href="#">推荐文章二</a></li>
</ul>
</aside>
<!-- 页脚 -->
<footer>
<p>版权所有 2026 网站名称</p>
</footer>
</body>
</html>
div 和 span
<div> 和 <span> 是没有语义的通用容器,用于组合元素、方便 CSS 样式控制。
<div>:块级元素,独占一行,用于大块区域划分<span>:行内元素,不换行,用于标记一行文字中的某个部分
<!-- div 用于划分区块 -->
<div class="card">
<div class="card-header">
<h3>卡片标题</h3>
</div>
<div class="card-body">
<p>卡片内容,这段文字中有<span class="highlight">一个重要词语</span>需要标红。</p>
</div>
</div>
HTML 属性
属性写在开标签内,格式为 属性名="属性值"。
| 属性 | 适用标签 | 说明 |
|---|---|---|
id |
所有标签 | 唯一标识符,同一页面中不能重复 |
class |
所有标签 | 类名,同一个类可以用于多个元素,CSS 通过类名来设置样式 |
style |
所有标签 | 内联样式,直接在标签上写 CSS(不推荐大量使用) |
href |
<a> |
链接目标地址 |
src |
<img>, <script> |
资源文件地址 |
alt |
<img> |
图片无法显示时的替代文字 |
placeholder |
<input>, <textarea> |
输入框为空时显示的提示文字 |
disabled |
表单元素 | 禁用该元素,用户无法操作 |
required |
表单元素 | 标记为必填项,提交时若为空会阻止提交 |
name |
表单元素 | 提交表单时该字段的键名 |
value |
表单元素 | 表单元素的值 |
type |
<input> |
输入框类型(text/password/email/number/radio/checkbox 等) |
target |
<a> |
链接打开方式(_blank 新标签页,_self 当前页) |
<!-- id 和 class 的使用 -->
<div id="main-header" class="header large-header">
<p class="intro-text">欢迎来到我的网站</p>
</div>
<!-- disabled 和 required -->
<form>
<input type="text" name="username" required placeholder="必填项" />
<input type="text" name="referral" disabled placeholder="此字段不可编辑" />
<button type="submit">提交</button>
</form>
路径:相对路径与绝对路径
假设项目文件结构如下:
my-website/
index.html
about.html
images/
photo.jpg
logo.png
css/
style.css
pages/
contact.html
绝对路径:从网站根目录或完整 URL 开始的路径。
<!-- 完整 URL(外部资源) -->
<img src="https://example.com/images/photo.jpg" alt="外部图片" />
<!-- 从网站根目录开始(以 / 开头) -->
<img src="/images/photo.jpg" alt="从根目录出发" />
<link rel="stylesheet" href="/css/style.css" />
相对路径:相对于当前文件所在位置的路径。
<!-- 在 index.html 中引用同级目录的文件 -->
<a href="about.html">关于我们</a>
<!-- 在 index.html 中引用子目录的文件 -->
<img src="images/photo.jpg" alt="子目录中的图片" />
<link rel="stylesheet" href="css/style.css" />
<!-- 在 pages/contact.html 中引用上级目录的图片 -->
<!-- ../ 表示返回上一级目录 -->
<img src="../images/logo.png" alt="标志" />
<!-- 返回两级目录 -->
<a href="../../index.html">返回首页</a>
表单完整示例
这是一个完整的用户注册页面:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户注册</title>
<style>
/* 简单样式,让表单好看一些 */
body {
font-family: sans-serif;
max-width: 500px;
margin: 40px auto;
padding: 0 20px;
}
.form-group {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 4px;
font-weight: bold;
}
input,
select,
textarea {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
.radio-group label,
.checkbox-group label {
font-weight: normal;
display: inline;
margin-left: 4px;
}
button {
padding: 10px 24px;
background-color: #0066cc;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #0055aa;
}
</style>
</head>
<body>
<h1>用户注册</h1>
<!-- action 留空表示提交到当前页面(实际开发中填写后端接口地址) -->
<form action="" method="post">
<!-- 用户名 -->
<div class="form-group">
<label for="username">用户名 *</label>
<input
type="text"
id="username"
name="username"
placeholder="4-16个字符"
minlength="4"
maxlength="16"
required
/>
</div>
<!-- 邮箱 -->
<div class="form-group">
<label for="email">邮箱 *</label>
<input
type="email"
id="email"
name="email"
placeholder="[email protected]"
required
/>
</div>
<!-- 密码 -->
<div class="form-group">
<label for="password">密码 *</label>
<input
type="password"
id="password"
name="password"
placeholder="至少8位"
minlength="8"
required
/>
</div>
<!-- 性别(单选) -->
<div class="form-group">
<label>性别</label>
<div class="radio-group">
<input type="radio" id="male" name="gender" value="male" />
<label for="male">男</label>
<input type="radio" id="female" name="gender" value="female" />
<label for="female">女</label>
<input type="radio" id="other" name="gender" value="other" />
<label for="other">其他</label>
</div>
</div>
<!-- 出生日期 -->
<div class="form-group">
<label for="birthday">出生日期</label>
<input type="date" id="birthday" name="birthday" />
</div>
<!-- 所在城市 -->
<div class="form-group">
<label for="city">所在城市</label>
<select id="city" name="city">
<option value="">请选择城市</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
<option value="shenzhen">深圳</option>
<option value="other">其他</option>
</select>
</div>
<!-- 兴趣爱好(复选) -->
<div class="form-group">
<label>兴趣爱好</label>
<div class="checkbox-group">
<input type="checkbox" id="hobby-reading" name="hobby" value="reading" />
<label for="hobby-reading">阅读</label>
<input type="checkbox" id="hobby-coding" name="hobby" value="coding" />
<label for="hobby-coding">编程</label>
<input type="checkbox" id="hobby-sports" name="hobby" value="sports" />
<label for="hobby-sports">运动</label>
<input type="checkbox" id="hobby-music" name="hobby" value="music" />
<label for="hobby-music">音乐</label>
</div>
</div>
<!-- 个人简介 -->
<div class="form-group">
<label for="bio">个人简介</label>
<textarea
id="bio"
name="bio"
rows="4"
placeholder="简单介绍一下自己(选填)"
maxlength="500"
></textarea>
</div>
<!-- 头像上传 -->
<div class="form-group">
<label for="avatar">上传头像</label>
<input type="file" id="avatar" name="avatar" accept="image/*" />
</div>
<!-- 同意条款 -->
<div class="form-group">
<input type="checkbox" id="agree" name="agree" required />
<label for="agree" style="display: inline; font-weight: normal;">
我已阅读并同意 <a href="/terms">用户协议</a>
</label>
</div>
<!-- 操作按钮 -->
<div class="form-group">
<button type="submit">立即注册</button>
<button type="reset" style="margin-left: 10px; background-color: #666;">重置</button>
</div>
</form>
</body>
</html>
CSS 是什么
CSS(Cascading Style Sheets,层叠样式表)用于控制网页的视觉外观。
CSS 的基本语法:
/* 选择器 { 属性: 值; } */
h1 {
color: red; /* 文字颜色设为红色 */
font-size: 32px; /* 字体大小 32 像素 */
text-align: center; /* 文字居中 */
}
CSS 三种引入方式
1. 内联样式(不推荐大量使用)
直接写在 HTML 标签的 style 属性里。优先级最高,但维护困难。
<p style="color: red; font-size: 18px;">这段文字是红色的。</p>
<h1 style="text-align: center; background-color: lightblue;">居中蓝底标题</h1>
2. 内部样式表
写在 <head> 里的 <style> 标签内。适合单个页面的样式。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>内部样式示例</title>
<style>
/* 这里写 CSS */
body {
font-family: "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
h1 {
color: #333333;
text-align: center;
}
p {
color: #666666;
line-height: 1.8;
}
</style>
</head>
<body>
<h1>标题</h1>
<p>段落文字。</p>
</body>
</html>
3. 外部样式表(推荐)
将 CSS 单独写在 .css 文件中,然后在 HTML 中用 <link> 引入。多个页面可以共用同一个 CSS 文件。
创建 style.css:
/* style.css */
body {
font-family: "微软雅黑", sans-serif;
background-color: #f5f5f5;
margin: 0;
padding: 0;
}
h1 {
color: #333333;
text-align: center;
}
在 HTML 中引入:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>外部样式示例</title>
<!-- 用 link 标签引入外部 CSS 文件 -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>标题</h1>
</body>
</html>
CSS 选择器
选择器决定 CSS 规则作用于哪些 HTML 元素。
基础选择器
/* 标签选择器:选中所有 p 标签 */
p {
color: #333;
}
/* 类选择器:选中所有 class="highlight" 的元素(以 . 开头) */
.highlight {
background-color: yellow;
}
/* ID 选择器:选中 id="header" 的元素(以 # 开头,每页唯一) */
#header {
background-color: #0066cc;
color: white;
}
/* 通配符选择器:选中所有元素(谨慎使用) */
* {
box-sizing: border-box;
}
组合选择器
/* 后代选择器(空格):选中 .card 内所有的 p 元素(包括多层嵌套) */
.card p {
font-size: 14px;
}
/* 子选择器(>):只选中 .nav 的直接子元素 li */
.nav > li {
display: inline-block;
}
/* 相邻兄弟选择器(+):选中紧跟在 h2 后面的那一个 p */
h2 + p {
font-weight: bold;
}
/* 通用兄弟选择器(~):选中 h2 后面所有的 p(同级) */
h2 ~ p {
color: #555;
}
/* 分组选择器(,):同时选中 h1、h2、h3 */
h1,
h2,
h3 {
font-family: "微软雅黑", sans-serif;
}
属性选择器
/* 选中有 disabled 属性的 input */
input[disabled] {
background-color: #eee;
}
/* 选中 type="text" 的 input */
input[type="text"] {
border: 1px solid #ccc;
}
/* 选中 href 以 https 开头的链接 */
a[href^="https"] {
color: green;
}
伪类选择器
伪类描述元素的特定状态。
/* :hover — 鼠标悬停时 */
a:hover {
color: orange;
text-decoration: underline;
}
button:hover {
background-color: darkblue;
}
/* :focus — 元素获得焦点时(如 input 被点击) */
input:focus {
outline: 2px solid #0066cc;
border-color: #0066cc;
}
/* :first-child — 选中父元素的第一个子元素 */
li:first-child {
font-weight: bold;
}
/* :last-child — 选中父元素的最后一个子元素 */
li:last-child {
border-bottom: none;
}
/* :nth-child(n) — 选中第 n 个子元素 */
tr:nth-child(2) {
background-color: #f0f0f0; /* 选中第2行 */
}
/* :nth-child(odd) 和 :nth-child(even) — 奇偶行 */
tr:nth-child(odd) {
background-color: #fafafa;
}
tr:nth-child(even) {
background-color: #f0f0f0;
}
/* :not() — 排除某些元素 */
li:not(:last-child) {
border-bottom: 1px solid #eee;
}
/* :checked — 被勾选的复选框或单选框 */
input:checked + label {
color: #0066cc;
}
完整示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>伪类示例</title>
<style>
ul {
list-style: none;
padding: 0;
max-width: 300px;
}
li {
padding: 10px;
border-bottom: 1px solid #eee;
transition: background-color 0.2s;
}
/* 鼠标悬停高亮 */
li:hover {
background-color: #f0f8ff;
}
/* 第一项加粗 */
li:first-child {
font-weight: bold;
}
/* 最后一项去掉底部边框 */
li:last-child {
border-bottom: none;
}
/* 奇数项背景 */
li:nth-child(odd) {
background-color: #fafafa;
}
</style>
</head>
<body>
<ul>
<li>第一项(加粗)</li>
<li>第二项</li>
<li>第三项</li>
<li>第四项</li>
<li>第五项(无底部边框)</li>
</ul>
</body>
</html>
伪元素选择器
伪元素在元素内部的特定位置插入内容,使用双冒号 :: 。
/* ::before — 在元素内容之前插入 */
.required-field::before {
content: "* ";
color: red;
}
/* ::after — 在元素内容之后插入 */
.price::after {
content: " 元";
font-size: 12px;
color: #999;
}
/* ::first-line — 段落的第一行 */
p::first-line {
font-weight: bold;
}
/* ::first-letter — 段落的第一个字母/字 */
p::first-letter {
font-size: 2em;
float: left;
margin-right: 4px;
}
/* ::placeholder — 输入框占位文字 */
input::placeholder {
color: #aaa;
font-style: italic;
}
<!-- ::before 和 ::after 使用示例 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>伪元素示例</title>
<style>
/* 用 ::before 给必填字段标红星号 */
.required::before {
content: "* ";
color: red;
}
/* 用 ::after 在价格后加单位 */
.price::after {
content: " 元";
font-size: 12px;
color: #999;
}
/* 用 ::before 和 ::after 制作装饰线 */
.section-title {
text-align: center;
position: relative;
}
.section-title::before,
.section-title::after {
content: "";
display: inline-block;
width: 60px;
height: 1px;
background-color: #ccc;
vertical-align: middle;
margin: 0 10px;
}
</style>
</head>
<body>
<label class="required">用户名</label>
<p>商品价格:<span class="price">299</span></p>
<h2 class="section-title">精选商品</h2>
</body>
</html>
盒模型
每个 HTML 元素都可以看成一个盒子,由四层组成(从内到外):
+------------------------------------------+
| margin(外边距) |
| +------------------------------------+ |
| | border(边框) | |
| | +------------------------------+ | |
| | | padding(内边距) | | |
| | | +------------------------+ | | |
| | | | content(内容区域) | | | |
| | | | width × height | | | |
| | | +------------------------+ | | |
| | +------------------------------+ | |
| +------------------------------------+ |
+------------------------------------------+
| 层 | 说明 |
|---|---|
| content | 实际内容区域,由 width 和 height 控制 |
| padding | 内容与边框之间的空白,背景色会延伸到 padding |
| border | 边框,可设置宽度、样式、颜色 |
| margin | 元素与其他元素之间的空白,透明无背景色 |
.box {
/* 内容区域尺寸 */
width: 300px;
height: 200px;
/* 内边距:上下 10px,左右 20px */
padding: 10px 20px;
/* 边框:2px 实线 深灰色 */
border: 2px solid #333;
/* 外边距:上下 16px,左右自动(实现水平居中) */
margin: 16px auto;
background-color: lightblue;
}
padding 和 margin 的简写规则
.box {
/* 四个方向相同 */
padding: 10px;
/* 上下 | 左右 */
padding: 10px 20px;
/* 上 | 左右 | 下 */
padding: 10px 20px 15px;
/* 上 | 右 | 下 | 左(顺时针) */
padding: 10px 20px 15px 5px;
/* margin 同理 */
margin: 0 auto; /* 常用:水平居中(上下0,左右自动) */
}
box-sizing
重要概念:默认情况下,width 只表示 content 的宽度,实际占用宽度 = width + padding + border。
使用 box-sizing: border-box 后,width 表示 content + padding + border 的总宽度,更符合直觉。
/* 推荐:在所有项目开头加上这段代码 */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* 演示区别 */
.default-box {
box-sizing: content-box; /* 默认值 */
width: 200px;
padding: 20px;
border: 2px solid black;
/* 实际占用宽度 = 200 + 20*2 + 2*2 = 244px */
}
.border-box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 2px solid black;
/* 实际占用宽度 = 200px(内容区会自动缩小) */
}
常用 CSS 属性
颜色
.example {
/* 颜色名称 */
color: red;
color: black;
color: transparent; /* 透明 */
/* 十六进制(最常用) */
color: #ff0000; /* 红色 */
color: #333333; /* 深灰色 */
color: #333; /* 简写:每两位相同时可缩短 */
/* RGB */
color: rgb(255, 0, 0); /* 红色 */
color: rgb(51, 51, 51); /* 深灰色 */
/* RGBA(最后一个参数是透明度,0-1) */
color: rgba(0, 0, 0, 0.5); /* 半透明黑色 */
/* HSL(色相/饱和度/亮度) */
color: hsl(0, 100%, 50%); /* 红色 */
}
字体
.text {
/* 字体大小 */
font-size: 16px;
font-size: 1rem; /* 相对于根元素字体大小(通常是16px) */
font-size: 1.5em; /* 相对于父元素字体大小 */
/* 字体族 */
font-family: "微软雅黑", "PingFang SC", sans-serif;
/* 写多个字体是备用方案,浏览器从左往右找可用的 */
/* 字重(粗细) */
font-weight: normal; /* 等同于 400 */
font-weight: bold; /* 等同于 700 */
font-weight: 300; /* 细体 */
font-weight: 600; /* 半粗体 */
/* 行高(影响行间距) */
line-height: 1.6; /* 推荐:无单位,相对于字体大小 */
line-height: 24px;
/* 字母/文字间距 */
letter-spacing: 2px;
/* 文字对齐 */
text-align: left;
text-align: center;
text-align: right;
text-align: justify; /* 两端对齐 */
/* 文字装饰 */
text-decoration: none; /* 去掉链接下划线 */
text-decoration: underline; /* 下划线 */
text-decoration: line-through; /* 删除线 */
/* 文字转换 */
text-transform: uppercase; /* 全大写 */
text-transform: lowercase; /* 全小写 */
/* 溢出省略号(单行) */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
背景
.box {
/* 背景颜色 */
background-color: #f5f5f5;
/* 背景图片 */
background-image: url("bg.jpg");
/* 背景尺寸 */
background-size: cover; /* 覆盖整个区域,可能裁剪 */
background-size: contain; /* 完整显示,可能留白 */
background-size: 100% 100%; /* 拉伸填满 */
/* 背景位置 */
background-position: center center;
background-position: top left;
/* 背景是否重复 */
background-repeat: no-repeat;
background-repeat: repeat-x; /* 只横向重复 */
/* 渐变背景 */
background: linear-gradient(to right, #0066cc, #00aaff);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
边框
.box {
/* 边框简写:宽度 样式 颜色 */
border: 1px solid #ccc;
border: 2px dashed red;
border: 3px dotted #333;
/* 单独设置某一侧 */
border-top: 2px solid black;
border-bottom: 1px solid #eee;
border-left: 4px solid #0066cc; /* 常用于卡片左侧色条 */
border-right: none;
/* 圆角 */
border-radius: 4px; /* 全部圆角 4px */
border-radius: 50%; /* 圆形(配合等宽高使用) */
border-radius: 8px 0 8px 0; /* 左上 右上 右下 左下 */
}
阴影
.box {
/* 盒阴影:x偏移 y偏移 模糊半径 扩散半径 颜色 */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
/* 多层阴影(用逗号分隔) */
box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 4px 12px rgba(0,0,0,0.05);
/* 内阴影 */
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
}
.text {
/* 文字阴影:x偏移 y偏移 模糊半径 颜色 */
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}
display 属性
display 控制元素的显示方式。
| 值 | 特点 |
|---|---|
block |
块级元素,独占一行,可设置宽高(div、p、h1-h6 的默认值) |
inline |
行内元素,不换行,不能设置宽高(span、a、strong 的默认值) |
inline-block |
行内排列,但可以设置宽高 |
none |
隐藏元素,不占据空间 |
flex |
弹性布局(见下一节) |
grid |
网格布局(见后续章节) |
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>display 示例</title>
<style>
/* 将 span 变成块级元素 */
.block-span {
display: block;
background-color: lightblue;
margin-bottom: 8px;
}
/* 将 div 变成行内元素 */
.inline-div {
display: inline;
background-color: lightgreen;
}
/* inline-block:行内排列,可设置宽高 */
.nav-item {
display: inline-block;
width: 80px;
height: 36px;
line-height: 36px;
text-align: center;
background-color: #0066cc;
color: white;
margin-right: 4px;
}
/* 隐藏元素 */
.hidden {
display: none;
}
</style>
</head>
<body>
<span class="block-span">我是 span,但现在独占一行</span>
<span class="block-span">我也是</span>
<div class="inline-div">div1</div>
<div class="inline-div">div2(行内排列)</div>
<div>
<span class="nav-item">首页</span>
<span class="nav-item">产品</span>
<span class="nav-item">关于</span>
</div>
<p class="hidden">这段文字被隐藏了,不占空间</p>
</body>
</html>
Flexbox 弹性布局
Flexbox 是目前最常用的 CSS 布局方式,用于在一个方向上排列元素。
给父元素设置 display: flex,其直接子元素自动成为 flex 子项。
父容器属性
.container {
display: flex;
/* 主轴方向(元素排列方向) */
flex-direction: row; /* 默认:水平从左到右 */
flex-direction: row-reverse; /* 水平从右到左 */
flex-direction: column; /* 垂直从上到下 */
flex-direction: column-reverse; /* 垂直从下到上 */
/* 主轴对齐(元素在主轴方向上的排列) */
justify-content: flex-start; /* 默认:靠左(或靠上) */
justify-content: flex-end; /* 靠右(或靠下) */
justify-content: center; /* 居中 */
justify-content: space-between; /* 两端对齐,间距均分 */
justify-content: space-around; /* 每个元素左右各有相同间距 */
justify-content: space-evenly; /* 所有间距完全相等 */
/* 交叉轴对齐(元素在垂直于主轴方向上的对齐) */
align-items: stretch; /* 默认:拉伸填满 */
align-items: flex-start; /* 顶部对齐 */
align-items: flex-end; /* 底部对齐 */
align-items: center; /* 垂直居中 */
align-items: baseline; /* 文字基线对齐 */
/* 换行 */
flex-wrap: nowrap; /* 默认:不换行(子项可能溢出或压缩) */
flex-wrap: wrap; /* 超出宽度自动换行 */
/* 间距(推荐用 gap 代替 margin) */
gap: 16px; /* 行间距和列间距都是 16px */
gap: 16px 24px; /* 行间距 16px,列间距 24px */
}
子项属性
.item {
/* 弹性增长:剩余空间的分配比例(0表示不增长) */
flex-grow: 0; /* 默认 */
flex-grow: 1; /* 占据剩余空间 */
/* 弹性收缩:空间不足时的收缩比例 */
flex-shrink: 1; /* 默认:允许收缩 */
flex-shrink: 0; /* 不收缩(保持原始大小) */
/* 基础尺寸 */
flex-basis: auto; /* 默认:由内容决定 */
flex-basis: 200px;
/* 简写:flex-grow flex-shrink flex-basis */
flex: 1; /* 等同于 flex: 1 1 0%,常用 */
flex: 0 0 200px; /* 固定宽度 200px,不伸缩 */
/* 单个子项的交叉轴对齐(覆盖父容器的 align-items) */
align-self: center;
align-self: flex-end;
/* 排列顺序(默认0,数字越小越靠前) */
order: -1; /* 排到最前面 */
order: 1; /* 排到后面 */
}
Flexbox 完整示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Flexbox 完整示例</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
padding: 20px;
background: #f5f5f5;
}
/* 示例1:水平导航栏 */
.navbar {
display: flex;
justify-content: space-between; /* logo 在左,菜单在右 */
align-items: center;
background: #333;
padding: 0 20px;
height: 60px;
}
.navbar .logo {
color: white;
font-size: 20px;
font-weight: bold;
}
.navbar .menu {
display: flex;
gap: 20px;
list-style: none;
}
.navbar .menu a {
color: #ccc;
text-decoration: none;
}
.navbar .menu a:hover {
color: white;
}
/* 示例2:卡片列表(自动换行) */
.card-list {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-top: 20px;
}
.card {
flex: 0 0 calc(33.333% - 11px); /* 三列布局 */
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card h3 {
margin-bottom: 8px;
color: #333;
}
.card p {
color: #666;
font-size: 14px;
line-height: 1.6;
}
/* 示例3:垂直居中 */
.center-box {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
background: #e8f4fd;
border-radius: 8px;
margin-top: 20px;
}
.center-box .content {
text-align: center;
}
</style>
</head>
<body>
<!-- 导航栏 -->
<nav class="navbar">
<div class="logo">MyBrand</div>
<ul class="menu">
<li><a href="#">首页</a></li>
<li><a href="#">产品</a></li>
<li><a href="#">关于</a></li>
<li><a href="#">联系</a></li>
</ul>
</nav>
<!-- 卡片列表 -->
<div class="card-list">
<div class="card">
<h3>卡片标题一</h3>
<p>这是卡片的描述文字,介绍卡片的内容。</p>
</div>
<div class="card">
<h3>卡片标题二</h3>
<p>这是卡片的描述文字,介绍卡片的内容。</p>
</div>
<div class="card">
<h3>卡片标题三</h3>
<p>这是卡片的描述文字,介绍卡片的内容。</p>
</div>
</div>
<!-- 垂直居中 -->
<div class="center-box">
<div class="content">
<h2>完美居中</h2>
<p>使用 Flexbox 轻松实现水平垂直居中</p>
</div>
</div>
</body>
</html>
Grid 网格布局
Grid 是二维布局系统,同时控制行和列,适合整体页面布局。
父容器属性
.grid-container {
display: grid;
/* 定义列:三列,每列 1fr(等分剩余空间) */
grid-template-columns: 1fr 1fr 1fr;
/* 简写:repeat(重复次数, 尺寸) */
grid-template-columns: repeat(3, 1fr);
/* 固定宽度和弹性宽度混合 */
grid-template-columns: 200px 1fr 1fr;
grid-template-columns: 250px 1fr; /* 左侧边栏 + 主内容 */
/* 定义行高 */
grid-template-rows: 60px 1fr 60px; /* 头部 主体 底部 */
grid-template-rows: auto; /* 自动高度 */
/* 间距 */
gap: 16px;
gap: 16px 24px; /* 行间距 列间距 */
/* 自动填充列(每列最少200px,自动适应数量) */
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
子项属性
.grid-item {
/* 指定列的起止位置(从第1列线到第3列线,跨越2列) */
grid-column: 1 / 3;
grid-column: 1 / span 2; /* 等价写法:从第1列开始跨2列 */
/* 指定行的起止位置 */
grid-row: 1 / 2;
/* 简写 */
grid-area: 1 / 1 / 2 / 3; /* row-start / col-start / row-end / col-end */
}
Grid 完整示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Grid 布局示例</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
min-height: 100vh;
padding: 20px;
background: #f0f0f0;
}
/* 示例1:页面整体布局 */
.page-layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 50px;
gap: 10px;
height: 500px;
}
/* header 跨越两列 */
.page-header {
grid-column: 1 / 3;
background: #333;
color: white;
display: flex;
align-items: center;
padding: 0 20px;
}
.page-sidebar {
background: #e8e8e8;
padding: 16px;
border-radius: 4px;
}
.page-main {
background: white;
padding: 20px;
border-radius: 4px;
}
/* footer 跨越两列 */
.page-footer {
grid-column: 1 / 3;
background: #333;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
}
/* 示例2:响应式图片网格 */
.photo-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 12px;
margin-top: 20px;
}
.photo-item {
aspect-ratio: 1; /* 保持正方形 */
background: #ccc;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #666;
}
</style>
</head>
<body>
<!-- 页面布局 -->
<div class="page-layout">
<header class="page-header">页头(跨两列)</header>
<aside class="page-sidebar">侧边栏</aside>
<main class="page-main">主内容区域</main>
<footer class="page-footer">页脚(跨两列)</footer>
</div>
<!-- 响应式图片网格 -->
<div class="photo-grid">
<div class="photo-item">1</div>
<div class="photo-item">2</div>
<div class="photo-item">3</div>
<div class="photo-item">4</div>
<div class="photo-item">5</div>
<div class="photo-item">6</div>
</div>
</body>
</html>
Position 定位
position 属性控制元素的定位方式。
| 值 | 说明 |
|---|---|
static |
默认值,按正常文档流排列,top/left 等无效 |
relative |
相对于自身原来位置偏移,原来的空间仍然保留 |
absolute |
相对于最近的非 static 祖先元素定位,脱离文档流 |
fixed |
相对于浏览器视口定位,固定在屏幕上不随滚动移动 |
sticky |
滚动到特定位置后固定(粘性定位) |
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>Position 示例</title>
<style>
body {
font-family: sans-serif;
height: 2000px; /* 制造滚动条 */
padding: 20px;
}
/* relative:相对自身位置偏移 */
.relative-box {
position: relative;
top: 10px; /* 向下偏移 10px */
left: 20px; /* 向右偏移 20px */
background: lightblue;
padding: 10px;
width: 200px;
}
/* absolute 的容器:必须设置 position: relative */
.card {
position: relative;
width: 200px;
height: 150px;
background: #f0f0f0;
border: 1px solid #ccc;
margin: 40px;
}
/* absolute:相对于 .card 定位(右上角角标) */
.badge {
position: absolute;
top: -8px; /* 超出父元素顶部 */
right: -8px; /* 超出父元素右侧 */
width: 24px;
height: 24px;
background: red;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
/* absolute:绝对居中(常用技巧) */
.centered {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* 将自身移回一半 */
font-weight: bold;
color: #333;
}
/* fixed:固定在右下角的悬浮按钮 */
.fab {
position: fixed;
bottom: 30px;
right: 30px;
width: 56px;
height: 56px;
background: #0066cc;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.4);
}
/* sticky:表格头部粘性定位 */
.sticky-header {
position: sticky;
top: 0; /* 距离视口顶部 0 时固定 */
background: #333;
color: white;
padding: 12px 20px;
z-index: 10;
}
</style>
</head>
<body>
<div class="relative-box">relative 定位,向右下偏移</div>
<div class="card">
<span class="badge">3</span>
<span class="centered">绝对居中文字</span>
</div>
<div class="sticky-header">向下滚动,我会粘在顶部</div>
<p style="margin-top: 20px;">继续滚动查看 sticky 效果……</p>
<!-- 固定悬浮按钮 -->
<div class="fab">+</div>
</body>
</html>
响应式设计
响应式设计让网页能在手机、平板、桌面等不同屏幕上都正常显示。
viewport meta 标签
在 <head> 中必须加入这行,否则手机会把页面缩小显示:
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
媒体查询
媒体查询根据屏幕宽度应用不同的样式。
/* 默认样式(手机优先,移动端先写) */
.container {
width: 100%;
padding: 0 16px;
}
.card-list {
display: flex;
flex-direction: column; /* 手机:单列 */
gap: 16px;
}
/* 平板:宽度 >= 768px */
@media (min-width: 768px) {
.container {
max-width: 768px;
margin: 0 auto;
}
.card-list {
flex-direction: row; /* 平板:横向排列 */
flex-wrap: wrap;
}
.card {
flex: 0 0 calc(50% - 8px); /* 两列 */
}
}
/* 桌面:宽度 >= 1024px */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
}
.card {
flex: 0 0 calc(33.333% - 11px); /* 三列 */
}
}
/* 也可以设置最大宽度(桌面优先写法) */
@media (max-width: 767px) {
.sidebar {
display: none; /* 手机上隐藏侧边栏 */
}
}
常用断点参考(不必死记,根据项目调整):
| 断点 | 说明 |
|---|---|
< 576px |
超小屏(手机竖屏) |
576px - 767px |
小屏(手机横屏) |
768px - 991px |
中屏(平板) |
992px - 1199px |
大屏(小桌面) |
>= 1200px |
超大屏(桌面显示器) |
完整响应式示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>响应式示例</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
background: #f5f5f5;
}
.container {
width: 100%;
padding: 0 16px;
margin: 0 auto;
}
/* 导航栏 */
.navbar {
background: #333;
padding: 12px 0;
}
.navbar .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
color: white;
font-size: 20px;
font-weight: bold;
}
/* 手机上隐藏菜单(简化示例,实际需要 JS 控制汉堡菜单) */
.menu {
display: none;
list-style: none;
gap: 20px;
}
.menu a {
color: #ccc;
text-decoration: none;
}
/* 卡片网格:手机单列 */
.card-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
padding: 20px 0;
}
.card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 平板:两列 */
@media (min-width: 768px) {
.container {
max-width: 960px;
}
.menu {
display: flex; /* 平板和桌面显示菜单 */
}
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 桌面:三列 */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
}
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}
</style>
</head>
<body>
<nav class="navbar">
<div class="container">
<div class="logo">MySite</div>
<ul class="menu">
<li><a href="#">首页</a></li>
<li><a href="#">产品</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>
</nav>
<div class="container">
<div class="card-grid">
<div class="card">
<h3>卡片一</h3>
<p>这是卡片内容。调整浏览器宽度查看响应式效果。</p>
</div>
<div class="card">
<h3>卡片二</h3>
<p>这是卡片内容。调整浏览器宽度查看响应式效果。</p>
</div>
<div class="card">
<h3>卡片三</h3>
<p>这是卡片内容。调整浏览器宽度查看响应式效果。</p>
</div>
<div class="card">
<h3>卡片四</h3>
<p>这是卡片内容。调整浏览器宽度查看响应式效果。</p>
</div>
</div>
</div>
</body>
</html>
过渡与动画
transition 过渡
过渡让属性值的变化平滑发生(而非瞬间跳变)。
/* 语法:transition: 属性名 时长 缓动函数 延迟; */
.button {
background-color: #0066cc;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
/* 所有属性变化都有 0.3s 的过渡 */
transition: all 0.3s ease;
/* 只对特定属性添加过渡 */
transition: background-color 0.3s ease, transform 0.2s ease;
}
.button:hover {
background-color: #0055aa;
transform: translateY(-2px); /* 向上移动 2px */
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.3);
}
常用缓动函数:
| 值 | 说明 |
|---|---|
ease |
默认:先快后慢 |
linear |
匀速 |
ease-in |
先慢后快 |
ease-out |
先快后慢 |
ease-in-out |
先慢,中间快,再慢 |
@keyframes 动画
@keyframes 定义动画关键帧,animation 属性应用动画。
/* 定义旋转动画 */
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* 定义渐入上浮动画 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 定义闪烁动画(多个关键帧) */
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
/* 应用动画 */
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f0f0f0;
border-top-color: #0066cc;
border-radius: 50%;
/* animation: 动画名 时长 缓动函数 延迟 次数 方向 */
animation: spin 1s linear infinite;
}
.hero-title {
animation: fadeInUp 0.6s ease-out;
}
.cta-button {
animation: pulse 2s ease-in-out infinite;
}
animation 属性详解:
| 子属性 | 说明 | 示例 |
|---|---|---|
animation-name |
@keyframes 名称 | spin |
animation-duration |
单次时长 | 1s |
animation-timing-function |
缓动函数 | ease |
animation-delay |
开始前等待时间 | 0.5s |
animation-iteration-count |
播放次数 | infinite / 3 |
animation-direction |
方向 | normal / reverse / alternate |
animation-fill-mode |
动画结束后状态 | forwards(保持结束状态) |
<!-- 过渡与动画完整示例 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>动画示例</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
padding: 40px;
background: #f5f5f5;
display: flex;
gap: 30px;
flex-wrap: wrap;
align-items: center;
}
/* 过渡按钮 */
.btn {
padding: 12px 24px;
background: #0066cc;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s ease,
transform 0.2s ease,
box-shadow 0.3s ease;
}
.btn:hover {
background: #0055aa;
transform: translateY(-3px);
box-shadow: 0 6px 16px rgba(0, 102, 204, 0.3);
}
.btn:active {
transform: translateY(0);
}
/* 旋转加载圈 */
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #e0e0e0;
border-top-color: #0066cc;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
/* 渐入卡片 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animated-card {
background: white;
border-radius: 8px;
padding: 20px;
width: 200px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
animation: fadeInUp 0.6s ease-out;
}
/* 心跳 */
@keyframes heartbeat {
0%, 100% { transform: scale(1); }
14% { transform: scale(1.15); }
28% { transform: scale(1); }
42% { transform: scale(1.15); }
70% { transform: scale(1); }
}
.heart {
font-size: 40px;
display: inline-block;
animation: heartbeat 1.5s ease-in-out infinite;
color: red;
}
</style>
</head>
<body>
<button class="btn">悬停查看过渡效果</button>
<div class="spinner"></div>
<div class="animated-card">
<h3>渐入卡片</h3>
<p>刷新页面查看动画</p>
</div>
<span class="heart">❤</span>
</body>
</html>
CSS 变量
CSS 变量(也叫自定义属性)让你定义可复用的值,统一管理颜色、间距等设计 token。
/* 变量定义在 :root 上,全局可用 */
:root {
/* 颜色变量 */
--color-primary: #0066cc;
--color-primary-hover: #0055aa;
--color-text: #333333;
--color-text-secondary: #666666;
--color-background: #f5f5f5;
--color-white: #ffffff;
--color-border: #e0e0e0;
/* 间距变量 */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
/* 圆角变量 */
--border-radius: 6px;
--border-radius-lg: 12px;
/* 字体大小 */
--font-size-sm: 14px;
--font-size-md: 16px;
--font-size-lg: 20px;
/* 阴影 */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* 使用变量:var(--变量名) */
.button {
background-color: var(--color-primary);
color: var(--color-white);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: var(--color-primary-hover);
}
.card {
background: var(--color-white);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
box-shadow: var(--shadow-md);
}
/* 在组件内覆盖变量(局部作用域) */
.dark-theme {
--color-background: #1a1a1a;
--color-text: #ffffff;
--color-white: #2a2a2a;
--color-border: #444444;
}
/* var() 第二个参数是默认值 */
.element {
color: var(--color-accent, #ff6600); /* 若 --color-accent 未定义,用 #ff6600 */
}
完整变量主题示例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>CSS 变量示例</title>
<style>
:root {
--primary: #0066cc;
--primary-hover: #0055aa;
--bg: #f5f5f5;
--surface: #ffffff;
--text: #333333;
--text-secondary: #666666;
--border: #e0e0e0;
--radius: 8px;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
/* 暗色主题:只需修改变量 */
.dark {
--primary: #4da6ff;
--bg: #1a1a2e;
--surface: #16213e;
--text: #e0e0e0;
--text-secondary: #aaaaaa;
--border: #333366;
--shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: var(--bg);
color: var(--text);
font-family: sans-serif;
padding: 30px;
min-height: 100vh;
transition: background 0.3s, color 0.3s;
}
.card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 24px;
max-width: 400px;
box-shadow: var(--shadow);
margin-bottom: 20px;
}
.card p {
color: var(--text-secondary);
margin-top: 8px;
}
.btn {
background: var(--primary);
color: white;
padding: 10px 20px;
border: none;
border-radius: var(--radius);
cursor: pointer;
margin-top: 16px;
transition: background 0.3s;
}
.btn:hover {
background: var(--primary-hover);
}
/* 切换主题的按钮 */
.theme-toggle {
background: var(--surface);
border: 1px solid var(--border);
color: var(--text);
padding: 8px 16px;
border-radius: var(--radius);
cursor: pointer;
margin-bottom: 20px;
}
</style>
</head>
<body id="theme-root">
<button class="theme-toggle" onclick="document.getElementById('theme-root').classList.toggle('dark')">
切换暗色/亮色主题
</button>
<div class="card">
<h2>CSS 变量实现主题切换</h2>
<p>点击上方按钮切换主题,所有颜色通过变量统一切换。</p>
<button class="btn">主要操作按钮</button>
</div>
</body>
</html>
综合实战:响应式个人主页
下面是一个完整的响应式个人主页,包含导航栏、Hero 区域、卡片列表、页脚。
将以下代码保存为 portfolio.html,直接用浏览器打开即可预览:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>张三 - 个人主页</title>
<style>
/* ========== 全局变量与重置 ========== */
:root {
--primary: #0066cc;
--primary-hover: #0055aa;
--dark: #1a1a2e;
--text: #333333;
--text-secondary: #666666;
--bg: #f8fafc;
--surface: #ffffff;
--border: #e2e8f0;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.1);
--radius: 10px;
--container-max: 1100px;
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth; /* 锚点跳转平滑滚动 */
}
body {
font-family: "PingFang SC", "微软雅黑", sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
}
/* ========== 工具类 ========== */
.container {
width: 100%;
max-width: var(--container-max);
margin: 0 auto;
padding: 0 20px;
}
/* ========== 导航栏 ========== */
.navbar {
position: sticky;
top: 0;
z-index: 100;
background: rgba(26, 26, 46, 0.95);
backdrop-filter: blur(8px); /* 毛玻璃效果 */
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.navbar .container {
display: flex;
justify-content: space-between;
align-items: center;
height: 64px;
}
.nav-logo {
font-size: 20px;
font-weight: 700;
color: #ffffff;
text-decoration: none;
letter-spacing: 1px;
}
.nav-menu {
display: flex;
gap: 32px;
list-style: none;
}
.nav-menu a {
color: rgba(255, 255, 255, 0.75);
text-decoration: none;
font-size: 15px;
transition: color 0.2s;
}
.nav-menu a:hover {
color: #ffffff;
}
/* ========== Hero 区域 ========== */
.hero {
background: linear-gradient(135deg, #1a1a2e 0%, #0f3460 60%, #0066cc 100%);
color: white;
padding: 100px 0;
text-align: center;
}
.hero-avatar {
width: 120px;
height: 120px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.15);
border: 3px solid rgba(255, 255, 255, 0.4);
margin: 0 auto 24px;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
/* 替换 background-image 为实际头像图片:
background-image: url("avatar.jpg");
background-size: cover; */
}
.hero h1 {
font-size: 42px;
font-weight: 700;
margin-bottom: 12px;
animation: fadeInUp 0.7s ease-out;
}
.hero .subtitle {
font-size: 18px;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 8px;
animation: fadeInUp 0.7s ease-out 0.1s both;
}
.hero .description {
font-size: 15px;
color: rgba(255, 255, 255, 0.65);
max-width: 500px;
margin: 0 auto 36px;
animation: fadeInUp 0.7s ease-out 0.2s both;
}
.hero-buttons {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
animation: fadeInUp 0.7s ease-out 0.3s both;
}
.btn-primary {
background: white;
color: var(--primary);
padding: 12px 28px;
border-radius: 50px;
text-decoration: none;
font-weight: 600;
font-size: 15px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
}
.btn-outline {
border: 2px solid rgba(255, 255, 255, 0.6);
color: white;
padding: 12px 28px;
border-radius: 50px;
text-decoration: none;
font-weight: 600;
font-size: 15px;
transition: border-color 0.2s, background 0.2s;
}
.btn-outline:hover {
border-color: white;
background: rgba(255, 255, 255, 0.1);
}
/* Hero 入场动画 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ========== 技能标签 ========== */
.skills-section {
padding: 60px 0;
background: var(--surface);
border-bottom: 1px solid var(--border);
}
.section-title {
text-align: center;
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
color: var(--text);
}
.section-subtitle {
text-align: center;
color: var(--text-secondary);
margin-bottom: 36px;
}
.skills-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
.skill-tag {
background: #f0f7ff;
color: var(--primary);
border: 1px solid #c3dafe;
padding: 8px 18px;
border-radius: 50px;
font-size: 14px;
font-weight: 500;
transition: background 0.2s, transform 0.2s;
}
.skill-tag:hover {
background: var(--primary);
color: white;
transform: translateY(-2px);
}
/* ========== 项目卡片区域 ========== */
.projects-section {
padding: 70px 0;
}
/* Grid 布局:手机1列,平板2列,桌面3列 */
.projects-grid {
display: grid;
grid-template-columns: 1fr;
gap: 24px;
}
.project-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.project-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
/* 卡片图片区域(用纯色代替实际图片) */
.card-image {
height: 180px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
.card-image.blue { background: linear-gradient(135deg, #0066cc, #00aaff); }
.card-image.purple { background: linear-gradient(135deg, #764ba2, #667eea); }
.card-image.green { background: linear-gradient(135deg, #11998e, #38ef7d); }
.card-content {
padding: 20px;
}
.card-content h3 {
font-size: 18px;
margin-bottom: 8px;
color: var(--text);
}
.card-content p {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.7;
margin-bottom: 16px;
}
.card-tags {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.card-tag {
background: var(--bg);
color: var(--text-secondary);
border: 1px solid var(--border);
padding: 3px 10px;
border-radius: 4px;
font-size: 12px;
}
.card-link {
display: inline-block;
color: var(--primary);
text-decoration: none;
font-size: 14px;
font-weight: 600;
transition: color 0.2s;
}
.card-link:hover {
color: var(--primary-hover);
text-decoration: underline;
}
/* ========== 关于区域 ========== */
.about-section {
padding: 70px 0;
background: var(--surface);
border-top: 1px solid var(--border);
}
.about-inner {
display: grid;
grid-template-columns: 1fr;
gap: 40px;
align-items: center;
}
.about-text h2 {
font-size: 28px;
font-weight: 700;
margin-bottom: 16px;
}
.about-text p {
color: var(--text-secondary);
margin-bottom: 16px;
line-height: 1.8;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
text-align: center;
}
.stat-item {
background: var(--bg);
border-radius: var(--radius);
padding: 20px 10px;
border: 1px solid var(--border);
}
.stat-number {
font-size: 32px;
font-weight: 700;
color: var(--primary);
display: block;
}
.stat-label {
font-size: 13px;
color: var(--text-secondary);
margin-top: 4px;
}
/* ========== 页脚 ========== */
.footer {
background: var(--dark);
color: rgba(255, 255, 255, 0.7);
padding: 40px 0;
text-align: center;
}
.footer-links {
display: flex;
justify-content: center;
gap: 24px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.footer-links a {
color: rgba(255, 255, 255, 0.6);
text-decoration: none;
font-size: 14px;
transition: color 0.2s;
}
.footer-links a:hover {
color: white;
}
.footer-copy {
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
}
/* ========== 响应式断点 ========== */
/* 平板及以上 */
@media (min-width: 768px) {
.hero h1 {
font-size: 52px;
}
.projects-grid {
grid-template-columns: repeat(2, 1fr);
}
.about-inner {
grid-template-columns: 1fr 1fr;
}
}
/* 桌面 */
@media (min-width: 1024px) {
.projects-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* 手机:隐藏导航菜单(实际项目需加汉堡菜单 JS) */
@media (max-width: 640px) {
.nav-menu {
display: none;
}
.hero {
padding: 60px 0;
}
.hero h1 {
font-size: 32px;
}
}
</style>
</head>
<body>
<!-- ========== 导航栏 ========== -->
<header class="navbar">
<div class="container">
<a href="#" class="nav-logo">ZS.DEV</a>
<nav>
<ul class="nav-menu">
<li><a href="#skills">技能</a></li>
<li><a href="#projects">项目</a></li>
<li><a href="#about">关于</a></li>
<li><a href="#contact">联系</a></li>
</ul>
</nav>
</div>
</header>
<!-- ========== Hero 区域 ========== -->
<section class="hero">
<div class="container">
<!-- 头像(替换为实际图片) -->
<div class="hero-avatar">Z</div>
<h1>你好,我是张三</h1>
<p class="subtitle">前端开发工程师</p>
<p class="description">
热爱构建精致、响应迅速的 Web 应用。<br />
专注于用户体验与现代前端技术。
</p>
<div class="hero-buttons">
<a href="#projects" class="btn-primary">查看我的项目</a>
<a href="#contact" class="btn-outline">联系我</a>
</div>
</div>
</section>
<!-- ========== 技能区域 ========== -->
<section class="skills-section" id="skills">
<div class="container">
<h2 class="section-title">技术栈</h2>
<p class="section-subtitle">我熟悉的技术与工具</p>
<div class="skills-grid">
<span class="skill-tag">HTML5</span>
<span class="skill-tag">CSS3</span>
<span class="skill-tag">JavaScript</span>
<span class="skill-tag">Vue3</span>
<span class="skill-tag">React</span>
<span class="skill-tag">Node.js</span>
<span class="skill-tag">Git</span>
<span class="skill-tag">Webpack</span>
<span class="skill-tag">Figma</span>
<span class="skill-tag">Python</span>
</div>
</div>
</section>
<!-- ========== 项目卡片区域 ========== -->
<section class="projects-section" id="projects">
<div class="container">
<h2 class="section-title">精选项目</h2>
<p class="section-subtitle">近期参与的项目</p>
<div class="projects-grid">
<!-- 项目卡片1 -->
<div class="project-card">
<div class="card-image blue">项目截图(可替换为 img 标签)</div>
<div class="card-content">
<h3>电商管理后台</h3>
<p>
基于 Vue3 + Element Plus 构建的后台管理系统,
包含商品管理、订单处理、数据可视化等功能模块。
</p>
<div class="card-tags">
<span class="card-tag">Vue3</span>
<span class="card-tag">Element Plus</span>
<span class="card-tag">ECharts</span>
</div>
<a href="#" class="card-link">查看详情</a>
</div>
</div>
<!-- 项目卡片2 -->
<div class="project-card">
<div class="card-image purple">项目截图(可替换为 img 标签)</div>
<div class="card-content">
<h3>个人博客系统</h3>
<p>
使用 React + Node.js 全栈开发,支持 Markdown 写作、
标签分类、评论互动,部署于云服务器。
</p>
<div class="card-tags">
<span class="card-tag">React</span>
<span class="card-tag">Node.js</span>
<span class="card-tag">MongoDB</span>
</div>
<a href="#" class="card-link">查看详情</a>
</div>
</div>
<!-- 项目卡片3 -->
<div class="project-card">
<div class="card-image green">项目截图(可替换为 img 标签)</div>
<div class="card-content">
<h3>实时天气应用</h3>
<p>
基于原生 JavaScript + OpenWeather API 开发,
支持城市搜索、实时天气、7 日预报,完全响应式。
</p>
<div class="card-tags">
<span class="card-tag">HTML/CSS/JS</span>
<span class="card-tag">REST API</span>
<span class="card-tag">响应式</span>
</div>
<a href="#" class="card-link">查看详情</a>
</div>
</div>
</div>
</div>
</section>
<!-- ========== 关于区域 ========== -->
<section class="about-section" id="about">
<div class="container">
<div class="about-inner">
<div class="about-text">
<h2>关于我</h2>
<p>
我是一名有 3 年经验的前端工程师,毕业于某某大学计算机科学专业。
热爱开源、持续学习,相信好的产品源于对细节的执着。
</p>
<p>
工作之余喜欢读书、跑步和捣鼓副业项目。
目前正在学习 Three.js 和 WebGL,探索 3D Web 的可能性。
</p>
</div>
<div class="stats-grid">
<div class="stat-item">
<span class="stat-number">3+</span>
<span class="stat-label">年工作经验</span>
</div>
<div class="stat-item">
<span class="stat-number">20+</span>
<span class="stat-label">完成项目</span>
</div>
<div class="stat-item">
<span class="stat-number">5</span>
<span class="stat-label">技术方向</span>
</div>
</div>
</div>
</div>
</section>
<!-- ========== 页脚 ========== -->
<footer class="footer" id="contact">
<div class="container">
<div class="footer-links">
<a href="mailto:[email protected]">邮件联系</a>
<a href="https://github.com/" target="_blank">GitHub</a>
<a href="https://juejin.cn/" target="_blank">掘金</a>
<a href="#">下载简历</a>
</div>
<p class="footer-copy">2026 张三. 用 HTML + CSS 构建.</p>
</div>
</footer>
</body>
</html>
最佳实践
HTML 语义化
使用语义化标签让浏览器、搜索引擎、屏幕阅读器都能理解页面结构:
| 语义标签 | 用途 | 不推荐的替代 |
|---|---|---|
<header> |
页面/区块的头部 | <div class="header"> |
<nav> |
导航链接区域 | <div class="nav"> |
<main> |
页面主要内容(每页唯一) | <div class="main"> |
<article> |
独立的内容单元(博文、评论) | <div class="article"> |
<section> |
内容分区(需有标题) | <div class="section"> |
<aside> |
侧边栏、补充内容 | <div class="sidebar"> |
<footer> |
页面/区块的底部 | <div class="footer"> |
<button> |
可点击操作按钮 | <div onclick="..."> |
CSS 组织原则
- BEM 命名:
block__element--modifier(如.card__title--highlighted),避免全局样式冲突 - 避免深层选择器:嵌套超过 3 层时考虑拆分为独立类名
- 变量集中管理:颜色、字体、间距用 CSS 自定义属性(
--primary-color: #3b82f6) - 移动优先:先写小屏样式,用
@media (min-width: 768px)扩展大屏
/* CSS 自定义属性示例 */
:root {
--color-primary: #3b82f6;
--color-text: #1f2937;
--spacing-base: 16px;
--border-radius: 8px;
}
.button {
background: var(--color-primary);
padding: var(--spacing-base);
border-radius: var(--border-radius);
}
布局选型
| 场景 | 推荐 | 原因 |
|---|---|---|
| 一维排列(行或列) | Flexbox | 简洁、对齐方便 |
| 二维网格布局 | Grid | 精确控制行列 |
| 文字环绕图片 | Float | 仍是此场景的正确用法 |
| 固定/绝对定位 | Position | 浮层、工具提示、导航栏 |
可访问性(a11y)基础
- 图片必须有
alt属性(装饰性图片用alt="") - 表单控件必须有关联的
<label> - 交互元素(按钮、链接)确保键盘可访问(
tabindex、focus样式) - 颜色对比度满足 WCAG AA 标准(正文文字对比度 ≥ 4.5:1)
注意事项与常见错误
HTML 注意事项
-
标签必须正确嵌套,不能交叉
<!-- 错误:交叉嵌套 --> <b><i>文字</b></i> <!-- 正确:先开后关 --> <b><i>文字</i></b> -
自闭合标签不需要结束标签
<!-- 正确写法 --> <img src="photo.jpg" alt="照片" /> <br /> <input type="text" /> <meta charset="UTF-8" /> -
alt 属性必须写,即使图片只是装饰
<!-- 装饰性图片:alt 留空(但属性本身要写) --> <img src="decoration.png" alt="" /> -
label 和 input 要关联,用
for属性指向 input 的id,点击 label 文字也能激活输入框<label for="email">邮箱</label> <input type="email" id="email" name="email" /> -
id 在同一页面中必须唯一,class 可以重复使用
-
不要用 HTML 标签来控制样式(如用
<br>制造间距、用<table>做非表格布局),样式交给 CSS 处理
CSS 注意事项
-
选择器优先级(specificity):当多条规则同时作用于一个元素时,优先级高的规则生效
优先级从高到低:
!important(尽量不用)- 内联样式(
style="") - ID 选择器(
#id) - 类选择器(
.class)、属性选择器、伪类 - 标签选择器(
div)、伪元素
/* 优先级低 */ p { color: black; } /* 优先级高,覆盖上面 */ .intro p { color: blue; } -
盒模型加 box-sizing 避免宽度计算错误
*, *::before, *::after { box-sizing: border-box; } -
外边距折叠(margin collapse):相邻的两个块级元素,上下 margin 会合并取较大值,而非相加
/* 上面元素 margin-bottom: 20px,下面元素 margin-top: 30px */ /* 实际间距是 30px,不是 50px */ -
z-index 只对 position 不为 static 的元素有效
/* 无效 */ .box { z-index: 999; /* position 是 static,z-index 不起作用 */ } /* 有效 */ .box { position: relative; z-index: 999; } -
不要滥用
!important,会破坏样式的可维护性,难以调试 -
Flexbox 和 Grid 中的子元素,
float和vertical-align会失效,不要混用 -
颜色对比度:文字颜色与背景颜色对比度要足够大,保证可读性(尤其是浅灰文字在白色背景上)
-
font-family 要写备用字体
/* 从左到右依次尝试,最后写通用字体族 */ font-family: "PingFang SC", "Hiragino Sans GB", "微软雅黑", sans-serif;
调试技巧
- 打开浏览器开发者工具(按 F12),在 Elements 面板查看 HTML 结构和 CSS 样式
- 用
outline: 1px solid red给元素加轮廓线,方便查看元素位置和大小(不占空间) - 在 Styles 面板可以实时修改 CSS 查看效果
常见陷阱
陷阱:CSS 盒模型导致元素实际宽度超出预期
现象: 设置 width: 200px 的元素比相邻元素宽,或加上 padding 后元素溢出父容器。
原因: 默认盒模型(box-sizing: content-box)中,width 只是内容区宽度,padding 和 border 会额外增加元素的实际占用宽度。200px 宽 + 20px padding × 2 = 实际 240px。
解决: 全局设置 box-sizing: border-box,使 width 包含 padding 和 border,更符合直觉。
*, *::before, *::after {
box-sizing: border-box;
}
陷阱:Flexbox / Grid 子元素 margin: auto 失效
现象: 在 Flex 或 Grid 容器的子元素上设置 margin: 0 auto,子元素没有居中,仍然靠左。
原因: Flex 容器默认 align-items: stretch,子元素已被拉伸对齐,margin: auto 在交叉轴上被覆盖。水平居中应使用容器的 justify-content: center,或子元素的 margin: auto(在 Flex 中,margin: auto 会吸收剩余空间,可实现居中)。
解决: 区分场景使用正确的居中方式。
/* 方式一:容器控制 */
.container {
display: flex;
justify-content: center; /* 主轴居中 */
align-items: center; /* 交叉轴居中 */
}
/* 方式二:子元素 margin auto(Flex 中有效) */
.child { margin: auto; }
陷阱:position: relative 父容器忘记设置,子元素绝对定位跑偏
现象: 给子元素设置 position: absolute; top: 0; left: 0 后,子元素跑到了页面左上角而不是父容器左上角。
原因: position: absolute 是相对于最近的"已定位祖先"(position 不为 static 的祖先)来定位的,如果没有,就相对于 <html> 根元素。
解决: 给父容器添加 position: relative(不改变布局流,只建立定位上下文)。
.parent {
position: relative; /* 建立定位上下文 */
}
.child {
position: absolute;
top: 0;
right: 0;
}