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入门


目录

  1. HTML 是什么
  2. 网页的基本结构
  3. 常用 HTML 标签
  4. HTML 属性
  5. 路径:相对路径与绝对路径
  6. 表单完整示例
  7. CSS 是什么
  8. CSS 三种引入方式
  9. CSS 选择器
  10. 盒模型
  11. 常用 CSS 属性
  12. display 属性
  13. Flexbox 弹性布局
  14. Grid 网格布局
  15. Position 定位
  16. 响应式设计
  17. 过渡与动画
  18. CSS 变量
  19. 综合实战:响应式个人主页
  20. 注意事项与常见错误

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 实际内容区域,由 widthheight 控制
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">&#10084;</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>
  • 交互元素(按钮、链接)确保键盘可访问(tabindexfocus 样式)
  • 颜色对比度满足 WCAG AA 标准(正文文字对比度 ≥ 4.5:1)

注意事项与常见错误

HTML 注意事项

  1. 标签必须正确嵌套,不能交叉

    <!-- 错误:交叉嵌套 -->
    <b><i>文字</b></i>
    
    <!-- 正确:先开后关 -->
    <b><i>文字</i></b>
    
  2. 自闭合标签不需要结束标签

    <!-- 正确写法 -->
    <img src="photo.jpg" alt="照片" />
    <br />
    <input type="text" />
    <meta charset="UTF-8" />
    
  3. alt 属性必须写,即使图片只是装饰

    <!-- 装饰性图片:alt 留空(但属性本身要写) -->
    <img src="decoration.png" alt="" />
    
  4. label 和 input 要关联,用 for 属性指向 input 的 id,点击 label 文字也能激活输入框

    <label for="email">邮箱</label>
    <input type="email" id="email" name="email" />
    
  5. id 在同一页面中必须唯一,class 可以重复使用

  6. 不要用 HTML 标签来控制样式(如用 <br> 制造间距、用 <table> 做非表格布局),样式交给 CSS 处理

CSS 注意事项

  1. 选择器优先级(specificity):当多条规则同时作用于一个元素时,优先级高的规则生效

    优先级从高到低:

    • !important(尽量不用)
    • 内联样式(style=""
    • ID 选择器(#id
    • 类选择器(.class)、属性选择器、伪类
    • 标签选择器(div)、伪元素
    /* 优先级低 */
    p { color: black; }
    
    /* 优先级高,覆盖上面 */
    .intro p { color: blue; }
    
  2. 盒模型加 box-sizing 避免宽度计算错误

    *,
    *::before,
    *::after {
      box-sizing: border-box;
    }
    
  3. 外边距折叠(margin collapse):相邻的两个块级元素,上下 margin 会合并取较大值,而非相加

    /* 上面元素 margin-bottom: 20px,下面元素 margin-top: 30px */
    /* 实际间距是 30px,不是 50px */
    
  4. z-index 只对 position 不为 static 的元素有效

    /* 无效 */
    .box {
      z-index: 999; /* position 是 static,z-index 不起作用 */
    }
    
    /* 有效 */
    .box {
      position: relative;
      z-index: 999;
    }
    
  5. 不要滥用 !important,会破坏样式的可维护性,难以调试

  6. Flexbox 和 Grid 中的子元素floatvertical-align 会失效,不要混用

  7. 颜色对比度:文字颜色与背景颜色对比度要足够大,保证可读性(尤其是浅灰文字在白色背景上)

  8. font-family 要写备用字体

    /* 从左到右依次尝试,最后写通用字体族 */
    font-family: "PingFang SC", "Hiragino Sans GB", "微软雅黑", sans-serif;
    

调试技巧

  1. 打开浏览器开发者工具(按 F12),在 Elements 面板查看 HTML 结构和 CSS 样式
  2. outline: 1px solid red 给元素加轮廓线,方便查看元素位置和大小(不占空间)
  3. 在 Styles 面板可以实时修改 CSS 查看效果


常见陷阱

陷阱:CSS 盒模型导致元素实际宽度超出预期

现象: 设置 width: 200px 的元素比相邻元素宽,或加上 padding 后元素溢出父容器。

原因: 默认盒模型(box-sizing: content-box)中,width 只是内容区宽度,paddingborder 会额外增加元素的实际占用宽度。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;
}

参见

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog