CSS 从入门到进阶:系统掌握现代网页样式与布局

CSS 是前端开发的三大基础技术之一。HTML 负责页面结构,CSS 负责页面表现,JavaScript 负责页面行为。本文将从基础语法、选择器、盒模型开始,逐步讲解 Flexbox、Grid、响应式设计、动画、CSS 变量、容器查询、层叠层、原生嵌套、性能优化和工程化实践。


1. CSS 是什么

CSS 的全称是 Cascading Style Sheets,中文通常翻译为"层叠样式表"。

CSS 用于描述 HTML 或 XML 文档在屏幕、纸张等媒介上的显示效果,包括:

  • 文字颜色和字体
  • 元素大小和间距
  • 页面布局
  • 背景、边框和阴影
  • 响应式适配
  • 过渡和动画
  • 深色模式
  • 打印样式

一个简单的页面可以分成三层:

text 复制代码
HTML:页面结构
CSS:页面样式
JavaScript:页面交互

例如下面的 HTML:

html 复制代码
<h1 class="title">学习 CSS</h1>

配合 CSS:

css 复制代码
.title {
  color: #2563eb;
  font-size: 32px;
  text-align: center;
}

浏览器会把标题显示成蓝色、32 像素并居中。

1.1 CSS 的发展方式

人们经常使用"CSS3"表示现代 CSS,但 CSS 并不是在 CSS3 之后整体升级成 CSS4。现代 CSS 已经被拆分为多个独立模块,例如:

  • CSS Color
  • CSS Grid Layout
  • CSS Flexible Box Layout
  • CSS Selectors
  • CSS Cascading and Inheritance
  • CSS Containment
  • CSS Transforms
  • CSS Animations

不同模块会按照自己的节奏发展。


2. CSS 的基本语法

2.1 CSS 规则结构

一条完整的 CSS 规则由选择器和声明块组成:

css 复制代码
选择器 {
  属性名: 属性值;
}

例如:

css 复制代码
.card {
  width: 320px;
  padding: 24px;
  background-color: white;
  border-radius: 12px;
}

其中:

  • .card 是选择器
  • widthpadding 是属性
  • 320px24px 是属性值
  • 每条声明通常以分号结束

2.2 CSS 注释

CSS 使用下面的注释语法:

css 复制代码
/* 这是一段 CSS 注释 */

CSS 不支持 JavaScript 风格的 // 单行注释。

2.3 在 HTML 中引入 CSS

方式一:行内样式
html 复制代码
<p style="color: red; font-size: 18px;">这是一段文字</p>

优点是简单直接,缺点是难以复用和维护,一般不建议在大型项目中大量使用。

方式二:内部样式表
html 复制代码
<head>
  <style>
    p {
      color: red;
      font-size: 18px;
    }
  </style>
</head>

适合简单演示或者只有一个 HTML 文件的小页面。

方式三:外部样式表
html 复制代码
<head>
  <link rel="stylesheet" href="./style.css">
</head>

style.css

css 复制代码
p {
  color: red;
  font-size: 18px;
}

外部样式表具有以下优点:

  • HTML 与样式分离
  • 多个页面可以复用
  • 更方便维护
  • 浏览器可以缓存 CSS 文件

在实际项目中通常优先使用外部样式表。

2.4 @import

CSS 也可以使用 @import 引入其他样式表:

css 复制代码
@import url("./reset.css");
@import url("./theme.css");

body {
  margin: 0;
}

@import 必须出现在普通样式规则之前。一般页面主样式仍建议通过 HTML 的 <link> 标签加载,由构建工具处理项目内部的样式依赖。


3. CSS 选择器

选择器用于确定一条 CSS 规则应该应用到哪些元素。

3.1 通配选择器

css 复制代码
* {
  box-sizing: border-box;
}

* 会匹配所有元素。

3.2 元素选择器

css 复制代码
p {
  line-height: 1.8;
}

匹配页面中的所有 <p> 元素。

3.3 类选择器

css 复制代码
.button {
  padding: 10px 20px;
}

对应 HTML:

html 复制代码
<button class="button">提交</button>

类选择器可以被多个元素复用,是日常开发中最常用的选择器之一。

3.4 ID 选择器

css 复制代码
#header {
  height: 64px;
}

对应 HTML:

html 复制代码
<header id="header"></header>

一个页面中的 ID 应当保持唯一。由于 ID 选择器优先级较高,不利于样式覆盖,因此组件样式通常更推荐使用类选择器。

3.5 属性选择器

css 复制代码
input[type="email"] {
  border-color: #2563eb;
}

a[target="_blank"] {
  color: #7c3aed;
}

[data-state="active"] {
  background-color: #dbeafe;
}

常见属性选择器:

css 复制代码
/* 存在某个属性 */
[disabled] {
  cursor: not-allowed;
}

/* 属性值完全相等 */
[type="text"] {
  background: white;
}

/* 属性值以某段文字开头 */
[href^="https"] {
  color: green;
}

/* 属性值以某段文字结尾 */
[href$=".pdf"] {
  color: red;
}

/* 属性值中包含某段文字 */
[class*="icon-"] {
  display: inline-block;
}

3.6 后代选择器

css 复制代码
.article p {
  line-height: 1.8;
}

匹配 .article 内部任意层级的 <p>

3.7 子元素选择器

css 复制代码
.menu > li {
  list-style: none;
}

只匹配 .menu 的直接子元素 <li>

3.8 相邻兄弟选择器

css 复制代码
h2 + p {
  margin-top: 8px;
}

匹配紧跟在 <h2> 后面的第一个 <p>

3.9 后续兄弟选择器

css 复制代码
h2 ~ p {
  color: #475569;
}

匹配与 <h2> 同级且位于它后面的所有 <p>

3.10 选择器分组

css 复制代码
h1,
h2,
h3 {
  font-family: system-ui, sans-serif;
}

可以把相同的样式同时应用给多个选择器。

3.11 组合选择器示例

css 复制代码
.article > p:first-of-type {
  font-size: 18px;
  color: #334155;
}

.form input[type="text"]:focus {
  border-color: #2563eb;
  outline: 3px solid rgb(37 99 235 / 20%);
}

选择器应该尽量简洁。过长的选择器会增加耦合,让 HTML 结构稍有变化就导致样式失效。


4. 层叠、优先级与继承

CSS 中的"C"代表 Cascading,也就是层叠。

当多个规则同时设置一个元素的同一个属性时,浏览器必须判断最终使用哪个值。W3C CSS Cascading and Inheritance Level 5 规定了声明值如何经过层叠、默认值和计算过程变成元素最终使用的样式。

4.1 层叠判断不只是优先级

可以把常见判断过程简化为:

  1. 声明是否适用于当前元素
  2. 来源和 !important
  3. 所在的层叠层
  4. 选择器优先级
  5. 作用域接近程度
  6. 代码出现顺序

对于普通业务代码,最常遇到的是选择器优先级和代码顺序。

4.2 选择器优先级

可以把常见选择器优先级记成三个部分:

text 复制代码
ID 选择器数量 - 类、属性、伪类数量 - 元素、伪元素数量

例如:

css 复制代码
/* 0-0-1 */
p {
  color: black;
}

/* 0-1-0 */
.text {
  color: blue;
}

/* 1-0-0 */
#message {
  color: red;
}

如果它们匹配的是同一个元素,通常 ID 选择器的规则会胜出。

下面的选择器优先级为 0-2-1

css 复制代码
.card .title:hover {
  color: blue;
}

其中:

  • .card 是一个类选择器
  • .title 是一个类选择器
  • :hover 是一个伪类
  • 这里没有元素选择器,因此准确来说是 0-3-0

4.3 同等优先级看出现顺序

css 复制代码
.title {
  color: blue;
}

.title {
  color: red;
}

两条规则优先级相同,后面的 color: red 生效。

4.4 行内样式

html 复制代码
<p class="message" style="color: red;">提示信息</p>

行内样式在普通作者样式中具有较高优先级,但仍然可能受到 !important、动画或不同样式来源规则的影响。

实际项目不应单纯依靠"优先级数字"理解完整层叠过程。

4.5 !important

css 复制代码
.button {
  color: white !important;
}

!important 会改变声明在层叠过程中的重要性。

不建议把它当成常规覆盖方案,因为它会让后续维护越来越困难。更好的处理方式是:

  • 降低选择器复杂度
  • 调整 CSS 加载顺序
  • 使用层叠层
  • 重新设计组件状态
  • 使用更明确的类名

4.6 继承

部分 CSS 属性会从父元素继承,例如:

  • color
  • font-family
  • font-size
  • line-height
  • text-align
  • visibility

部分属性通常不会继承,例如:

  • width
  • height
  • margin
  • padding
  • border
  • background
  • position

示例:

css 复制代码
body {
  color: #334155;
  font-family: system-ui, sans-serif;
}

页面中的大多数文本元素会继承这些设置。

4.7 CSS 全局关键字

css 复制代码
.example {
  color: inherit;
  margin: initial;
  border: unset;
  background: revert;
}

常用关键字:

关键字 含义
inherit 使用父元素的计算值
initial 使用该属性规范定义的初始值
unset 可继承属性按继承处理,否则使用初始值
revert 回退到较低层叠来源的结果
revert-layer 回退当前层叠层造成的影响

注意:initial 不等于浏览器样式表为某个 HTML 标签设置的默认表现。


5. CSS 的颜色与单位

5.1 颜色表示方式

颜色名称
css 复制代码
.title {
  color: red;
}
十六进制
css 复制代码
.title {
  color: #2563eb;
}

带透明度的八位十六进制:

css 复制代码
.overlay {
  background-color: #00000080;
}
RGB
css 复制代码
.title {
  color: rgb(37 99 235);
}

.overlay {
  background-color: rgb(15 23 42 / 60%);
}
HSL
css 复制代码
.title {
  color: hsl(221 83% 53%);
}
currentColor

currentColor 表示元素当前的 color 值:

css 复制代码
.button {
  color: #2563eb;
  border: 1px solid currentColor;
}

修改文字颜色时,边框也会自动变化。

5.2 绝对长度单位

css 复制代码
.box {
  width: 300px;
  border-width: 1px;
}

网页开发中最常用的绝对长度单位是 px

CSS 像素是一个逻辑单位,不一定等同于设备上的一个物理像素。

5.3 相对单位

单位 含义
% 相对于相关参考值
em 通常相对于当前元素的字体尺寸
rem 相对于根元素的字体尺寸
vw 视口宽度的 1%
vh 视口高度的 1%
dvh 动态视口高度的 1%
svh 小视口高度的 1%
lvh 大视口高度的 1%
ch 近似基于当前字体中数字 0 的宽度
fr Grid 布局中的剩余空间份额

示例:

css 复制代码
html {
  font-size: 16px;
}

.article {
  width: min(90%, 72rem);
  margin-inline: auto;
}

.article h1 {
  font-size: 2.5rem;
}

.article p {
  max-width: 70ch;
}

5.4 如何选择单位

常见建议:

  • 边框可以使用 px
  • 字体和间距可使用 rem
  • 局部尺寸可使用 %
  • Grid 剩余空间使用 fr
  • 全屏区域可使用 dvh
  • 流式尺寸可以使用 clamp()
  • 不要机械地把所有 px 替换成 rem

6. CSS 盒模型

浏览器通常会把元素渲染成一个矩形盒子。

盒模型从内到外包括:

text 复制代码
content → padding → border → margin

6.1 标准盒模型

css 复制代码
.box {
  width: 300px;
  padding: 20px;
  border: 5px solid #2563eb;
}

默认情况下:

css 复制代码
box-sizing: content-box;

实际占用宽度为:

text 复制代码
300 + 20 × 2 + 5 × 2 = 350px

外边距不属于盒子自身宽度,但会影响元素与周围内容之间的距离。

6.2 border-box

css 复制代码
.box {
  box-sizing: border-box;
  width: 300px;
  padding: 20px;
  border: 5px solid #2563eb;
}

此时声明的 width: 300px 已经包含内容区、内边距和边框。

实际项目通常会使用:

css 复制代码
*,
*::before,
*::after {
  box-sizing: border-box;
}

6.3 外边距

css 复制代码
.card {
  margin: 20px;
}

简写规则:

css 复制代码
/* 上下左右 */
margin: 20px;

/* 上下、左右 */
margin: 20px 30px;

/* 上、左右、下 */
margin: 10px 20px 30px;

/* 上、右、下、左 */
margin: 10px 20px 30px 40px;

padding 的简写规则相同。

6.4 水平居中

具有确定或受约束宽度的块级元素可以使用:

css 复制代码
.container {
  width: min(90%, 1200px);
  margin-inline: auto;
}

6.5 外边距折叠

普通文档流中,部分相邻块级元素的垂直外边距可能发生折叠:

css 复制代码
.first {
  margin-bottom: 30px;
}

.second {
  margin-top: 20px;
}

最终间距可能是 30px,而不是 50px

Flex、Grid 项目之间的外边距不会按照普通块级布局的方式折叠。现代布局中更推荐通过父容器的 gap 控制项目间距。

6.6 min-widthmax-width

css 复制代码
.card {
  width: 100%;
  max-width: 420px;
}

.sidebar {
  min-width: 240px;
}

响应式设计中,通常比固定宽度更常使用:

css 复制代码
width: 100%;
max-width: 1200px;

7. 文本、字体与背景

7.1 字体设置

css 复制代码
body {
  font-family:
    Inter,
    "PingFang SC",
    "Microsoft YaHei",
    system-ui,
    sans-serif;
}

浏览器会按照顺序寻找可用字体。

7.2 字体大小与行高

css 复制代码
.article {
  font-size: 16px;
  line-height: 1.75;
}

无单位行高会根据元素自己的字体大小计算,并且在继承时通常更灵活。

7.3 字体粗细

css 复制代码
.title {
  font-weight: 700;
}

.description {
  font-weight: 400;
}

常见值:

text 复制代码
100、200、300、400、500、600、700、800、900

具体效果取决于字体是否包含对应字重。

7.4 文本对齐与装饰

css 复制代码
.title {
  text-align: center;
}

.link {
  text-decoration: none;
}

.price {
  text-decoration: line-through;
}

7.5 文本溢出省略

单行省略:

css 复制代码
.single-line {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

多行省略常见写法:

css 复制代码
.multi-line {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}

使用多行截断时,要确认目标浏览器兼容性,并确保用户仍有方式读取完整内容。

7.6 自动换行

css 复制代码
.content {
  overflow-wrap: anywhere;
}

这对于长链接、哈希值和连续英文字符非常有用。

7.7 背景

css 复制代码
.hero {
  background-color: #0f172a;
  background-image: url("./images/banner.jpg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}

渐变背景:

css 复制代码
.hero {
  background:
    linear-gradient(
      135deg,
      rgb(37 99 235 / 95%),
      rgb(124 58 237 / 90%)
    );
}

多层背景:

css 复制代码
.hero {
  background:
    linear-gradient(rgb(15 23 42 / 65%), rgb(15 23 42 / 65%)),
    url("./images/banner.jpg") center / cover no-repeat;
}

7.8 object-fit

图片在固定区域中可以使用:

css 复制代码
.avatar {
  width: 96px;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 50%;
}

常用值:

  • cover:填满容器,可能裁剪
  • contain:完整显示,可能留白
  • fill:拉伸填满
  • none:保持原始尺寸

8. 文档流与 display

8.1 普通文档流

如果没有特殊布局规则:

  • 块级元素通常从上到下排列
  • 行内元素通常在一行内排列
  • 元素会根据内容和可用空间占据位置

理解普通文档流非常重要。很多布局问题并不需要使用定位解决。

8.2 常用 display

css 复制代码
.block {
  display: block;
}

.inline {
  display: inline;
}

.inline-block {
  display: inline-block;
}

.flex {
  display: flex;
}

.grid {
  display: grid;
}

.hidden {
  display: none;
}

8.3 block

块级盒子通常:

  • 独占一行
  • 默认宽度会填充可用空间
  • 可以设置宽高和外边距

8.4 inline

行内盒子通常:

  • 与文本在一行内排列
  • 宽高主要由内容决定
  • 上下方向的尺寸行为与块级盒子不同

8.5 inline-block

inline-block 同时具有部分行内和块级特性:

  • 可以与其他元素排列在一行
  • 可以设置宽高
  • 可以设置完整的内外边距

8.6 隐藏元素

css 复制代码
.hidden {
  display: none;
}

元素不会显示,也不占据布局空间,并且通常不会进入无障碍树。

css 复制代码
.invisible {
  visibility: hidden;
}

元素不可见,但仍然占据布局空间。

css 复制代码
.transparent {
  opacity: 0;
}

元素完全透明,但仍占据空间,也可能继续接收鼠标或键盘交互。

如果需要隐藏但保留给屏幕阅读器,可以使用专门的视觉隐藏样式:

css 复制代码
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  white-space: nowrap;
  border: 0;
  clip-path: inset(50%);
}

9. 定位与层叠上下文

9.1 position: static

css 复制代码
.element {
  position: static;
}

这是大多数元素的默认定位方式。

9.2 相对定位

css 复制代码
.element {
  position: relative;
  top: 10px;
  left: 20px;
}

元素仍保留原来的布局空间,只在视觉上发生偏移。

更常见的用途是给绝对定位子元素建立包含块:

css 复制代码
.card {
  position: relative;
}

.card-badge {
  position: absolute;
  top: 12px;
  right: 12px;
}

9.3 绝对定位

css 复制代码
.tooltip {
  position: absolute;
  top: 100%;
  left: 0;
}

绝对定位元素会脱离普通文档流。它通常相对于最近的、能建立定位包含块的祖先进行定位。

9.4 固定定位

css 复制代码
.back-to-top {
  position: fixed;
  right: 24px;
  bottom: 24px;
}

固定定位通常相对于视口定位,但某些祖先的变换、过滤和包含属性可能改变它的包含块。

9.5 粘性定位

css 复制代码
.navbar {
  position: sticky;
  top: 0;
  z-index: 10;
}

sticky 会在普通布局位置和指定边界之间切换。

粘性定位失效时,可以检查:

  • 是否设置了 topbottom 等偏移值
  • 祖先元素是否设置了 overflow
  • 容器是否有足够的滚动距离
  • 粘性元素是否和容器一样高
  • 滚动容器是否符合预期

9.6 z-index

css 复制代码
.modal {
  position: fixed;
  z-index: 1000;
}

z-index 不是一个全局绝对排名。元素会先在自己的层叠上下文中排序。

可能创建层叠上下文的条件包括:

  • 根元素
  • 某些带 z-index 的定位元素
  • position: fixed
  • position: sticky
  • opacity 小于 1
  • transform 不为 none
  • filter 不为 none
  • isolation: isolate
  • 某些 contain 设置

z-index: 999999 仍然被遮挡时,应检查祖先元素所在的层叠上下文,而不是继续增大数字。

9.7 建立独立层叠上下文

css 复制代码
.component {
  isolation: isolate;
}

这样可以让组件内部的 z-index 更容易管理,避免与页面其他区域相互干扰。


10. Flexbox 弹性布局

Flexbox 适合一维布局,即主要沿行或者列的方向排列元素。W3C Flexbox 规范 定义了弹性容器、主轴、交叉轴、伸缩和对齐行为。

10.1 创建弹性容器

css 复制代码
.container {
  display: flex;
}

直接子元素会成为 Flex 项目。

10.2 主轴方向

css 复制代码
.container {
  display: flex;
  flex-direction: row;
}

常用值:

  • row
  • row-reverse
  • column
  • column-reverse

10.3 主轴对齐

css 复制代码
.container {
  display: flex;
  justify-content: center;
}

常用值:

  • flex-start
  • flex-end
  • center
  • space-between
  • space-around
  • space-evenly

10.4 交叉轴对齐

css 复制代码
.container {
  display: flex;
  align-items: center;
}

常用值:

  • stretch
  • flex-start
  • flex-end
  • center
  • baseline

10.5 换行

css 复制代码
.container {
  display: flex;
  flex-wrap: wrap;
}

10.6 项目间距

css 复制代码
.container {
  display: flex;
  gap: 16px;
}

相比给每个子元素单独设置外边距,gap 更直观,也不会在容器边缘产生多余间距。

10.7 flex 简写

css 复制代码
.item {
  flex: 1 1 240px;
}

依次表示:

text 复制代码
flex-grow flex-shrink flex-basis

含义:

  • flex-grow:剩余空间的扩张比例
  • flex-shrink:空间不足时的收缩比例
  • flex-basis:参与伸缩计算前的基础尺寸

常用写法:

css 复制代码
.item {
  flex: 1;
}

通常用于让多个项目平均分配剩余空间。

10.8 单独控制项目

css 复制代码
.item {
  align-self: flex-end;
}

排序:

css 复制代码
.item-featured {
  order: -1;
}

不要为了视觉顺序随意改变 order,因为视觉顺序和 DOM、键盘焦点、屏幕阅读器顺序可能不一致。

10.9 Flex 项目内容溢出

Flex 项目的自动最小尺寸可能导致长文本撑开容器:

css 复制代码
.content {
  min-width: 0;
}

单行省略常见组合:

css 复制代码
.content {
  min-width: 0;
}

.content-title {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

10.10 常见布局

水平垂直居中:

css 复制代码
.center {
  display: flex;
  justify-content: center;
  align-items: center;
}

左右布局:

css 复制代码
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

自适应卡片:

css 复制代码
.card-list {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}

.card {
  flex: 1 1 280px;
}

11. Grid 网格布局

Grid 适合二维布局,可以同时控制行和列。W3C CSS Grid Layout Level 2 在网格布局基础上定义了 subgrid 等能力。

11.1 创建网格容器

css 复制代码
.container {
  display: grid;
}

11.2 定义列

css 复制代码
.container {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
}

表示:

  • 第一列固定为 200px
  • 第二列和第三列平均分配剩余空间

11.3 repeat()

css 复制代码
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

等价于:

css 复制代码
grid-template-columns: 1fr 1fr 1fr;

11.4 设置间距

css 复制代码
.container {
  display: grid;
  gap: 24px;
}

也可以分别设置:

css 复制代码
.container {
  row-gap: 16px;
  column-gap: 24px;
}

11.5 响应式卡片网格

css 复制代码
.card-list {
  display: grid;
  grid-template-columns: repeat(
    auto-fit,
    minmax(min(100%, 280px), 1fr)
  );
  gap: 24px;
}

这段代码表示:

  • 每列尽量不小于 280px
  • 一行能放几列就放几列
  • 剩余空间平均分配
  • 容器小于 280px 时不强行溢出

11.6 auto-fitauto-fill

两者都会根据容器空间重复创建列。

可以简单理解为:

  • auto-fill:尽量保留可容纳的网格轨道
  • auto-fit:会折叠空轨道,让已有项目扩展

11.7 网格项目跨行跨列

css 复制代码
.featured {
  grid-column: span 2;
  grid-row: span 2;
}

指定起止线:

css 复制代码
.header {
  grid-column: 1 / -1;
}

-1 表示最后一条显式网格线。

11.8 命名区域

css 复制代码
.page {
  display: grid;
  grid-template:
    "header header" auto
    "sidebar main" 1fr
    "footer footer" auto
    / 240px 1fr;
  min-height: 100dvh;
}

.header {
  grid-area: header;
}

.sidebar {
  grid-area: sidebar;
}

.main {
  grid-area: main;
}

.footer {
  grid-area: footer;
}

11.9 minmax()

css 复制代码
.layout {
  display: grid;
  grid-template-columns: minmax(220px, 280px) minmax(0, 1fr);
}

第二列使用 minmax(0, 1fr) 可以减少内容因为自动最小尺寸而撑破布局的情况。

11.10 subgrid

父网格:

css 复制代码
.card-list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

卡片内部使用父网格轨道:

css 复制代码
.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
}

subgrid 适合让不同卡片内部的标题、正文和按钮保持对齐。使用前应结合项目支持范围检查目标浏览器。

11.11 Flexbox 和 Grid 如何选择

使用 Flexbox:

  • 主要是一行或一列
  • 导航栏
  • 按钮组
  • 图标与文字
  • 简单的水平或垂直对齐

使用 Grid:

  • 同时控制行和列
  • 卡片网格
  • 页面整体布局
  • 仪表盘
  • 需要跨行跨列的复杂布局

两者不是竞争关系,实际项目经常组合使用。


12. 响应式设计

响应式设计的目标是让页面在手机、平板、桌面端和不同窗口尺寸下都能正常使用。

12.1 设置视口

HTML 中需要设置:

html 复制代码
<meta
  name="viewport"
  content="width=device-width, initial-scale=1"
>

12.2 移动优先

基础样式先面向小屏幕:

css 复制代码
.card-list {
  display: grid;
  grid-template-columns: 1fr;
}

屏幕变宽后增强布局:

css 复制代码
@media (min-width: 48rem) {
  .card-list {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 64rem) {
  .card-list {
    grid-template-columns: repeat(3, 1fr);
  }
}

12.3 不要只按设备名称设计断点

不要简单理解成:

text 复制代码
手机:固定一个宽度
平板:固定一个宽度
电脑:固定一个宽度

更好的方式是根据内容在什么时候开始拥挤、换行或者失衡来设置断点。

12.4 媒体查询

根据宽度:

css 复制代码
@media (min-width: 768px) {
  .sidebar {
    display: block;
  }
}

根据横竖屏:

css 复制代码
@media (orientation: landscape) {
  .hero {
    min-height: 70vh;
  }
}

根据用户深色模式偏好:

css 复制代码
@media (prefers-color-scheme: dark) {
  :root {
    --page-bg: #0f172a;
    --text-color: #e2e8f0;
  }
}

根据用户减少动态效果的偏好:

css 复制代码
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

12.5 流式排版

css 复制代码
.hero-title {
  font-size: clamp(2rem, 5vw, 4.5rem);
}

含义:

  • 最小 2rem
  • 理想值 5vw
  • 最大 4.5rem

12.6 响应式容器

css 复制代码
.container {
  width: min(100% - 32px, 1200px);
  margin-inline: auto;
}

这样可以同时实现:

  • 小屏幕两侧保留 16px
  • 大屏幕最大宽度为 1200px
  • 容器水平居中

12.7 响应式图片

css 复制代码
img {
  display: block;
  max-width: 100%;
  height: auto;
}

对于固定比例的封面:

css 复制代码
.cover {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

12.8 逻辑属性

传统写法:

css 复制代码
.card {
  margin-left: 16px;
  padding-right: 24px;
}

逻辑属性写法:

css 复制代码
.card {
  margin-inline-start: 16px;
  padding-inline-end: 24px;
}

常用逻辑属性:

css 复制代码
.element {
  margin-inline: auto;
  margin-block: 24px;
  padding-inline: 20px;
  padding-block: 16px;
  border-inline-start: 4px solid #2563eb;
}

逻辑属性能更好地适配从右到左文字和不同书写模式。


13. 容器查询

媒体查询关注视口,容器查询关注组件所在容器的尺寸。

这意味着同一个组件放在主内容区和侧边栏时,可以根据自己的可用空间改变布局,而不是只看整个浏览器窗口。

容器查询是 CSS Containment 模块的重要能力,具体语义可以参考 W3C CSS Containment Module Level 3

13.1 创建查询容器

css 复制代码
.feature-section {
  container-name: features;
  container-type: inline-size;
}

也可以使用简写:

css 复制代码
.feature-section {
  container: features / inline-size;
}

13.2 查询容器宽度

css 复制代码
.feature-grid {
  display: grid;
  gap: 16px;
}

@container features (min-width: 48rem) {
  .feature-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

注意:容器查询主要影响查询容器的后代元素,不能用尺寸查询直接修改作为查询对象的容器自身。

13.3 容器查询单位

常见容器单位:

单位 含义
cqw 查询容器宽度的 1%
cqh 查询容器高度的 1%
cqi 查询容器行内轴尺寸的 1%
cqb 查询容器块轴尺寸的 1%
cqmin cqicqb 中较小的值
cqmax cqicqb 中较大的值

示例:

css 复制代码
.feature-title {
  font-size: clamp(1.25rem, 5cqi, 2.5rem);
}

13.4 媒体查询与容器查询的选择

媒体查询适合:

  • 页面整体布局
  • 视口级导航切换
  • 全局字号和页面边距
  • 用户偏好查询

容器查询适合:

  • 可复用卡片
  • 侧边栏组件
  • 独立业务模块
  • 同一组件在不同容器中的布局变化

14. 伪类与伪元素

14.1 交互伪类

css 复制代码
.button:hover {
  background-color: #1d4ed8;
}

.button:active {
  transform: translateY(1px);
}

.input:focus {
  border-color: #2563eb;
}

14.2 :focus-visible

css 复制代码
.button:focus-visible {
  outline: 3px solid rgb(37 99 235 / 35%);
  outline-offset: 3px;
}

:focus-visible 主要在浏览器判断需要显示键盘焦点提示时匹配。

不要直接对所有元素设置:

css 复制代码
*:focus {
  outline: none;
}

这样会让键盘用户难以判断当前焦点位置。

14.3 结构伪类

css 复制代码
.list li:first-child {
  font-weight: 700;
}

.list li:last-child {
  border-bottom: 0;
}

.list li:nth-child(odd) {
  background-color: #f8fafc;
}

.list li:nth-child(3n) {
  color: #2563eb;
}

14.4 状态伪类

css 复制代码
input:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

input:checked + label {
  color: #2563eb;
}

input:required {
  border-inline-start: 3px solid #f59e0b;
}

14.5 :not()

css 复制代码
.navigation a:not(.active) {
  color: #64748b;
}

14.6 :is()

传统写法:

css 复制代码
.article h1,
.article h2,
.article h3 {
  line-height: 1.3;
}

使用 :is()

css 复制代码
.article :is(h1, h2, h3) {
  line-height: 1.3;
}

14.7 :where()

css 复制代码
:where(.article, .page-content) a {
  color: #2563eb;
}

:where() 本身以及参数列表不会增加选择器优先级,适合编写容易覆盖的基础规则。

14.8 :has()

根据子元素或后续关系选择父级:

css 复制代码
.card:has(.card-image) {
  padding-top: 0;
}

.form-field:has(input:focus) {
  border-color: #2563eb;
}

.form-field:has(input:invalid:not(:placeholder-shown)) {
  border-color: #dc2626;
}

:has():is():where() 等现代选择器由 W3C Selectors Level 4 定义。使用复杂关系选择器时,应避免匹配范围过大,并结合项目目标浏览器验证支持情况。

14.9 ::before::after

css 复制代码
.link::after {
  content: " ↗";
}

.title::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 1em;
  margin-inline-end: 10px;
  background: #2563eb;
  border-radius: 999px;
  vertical-align: -0.1em;
}

伪元素常用于:

  • 装饰线
  • 图标背景
  • 遮罩
  • 气泡箭头
  • 不承载核心含义的辅助内容

重要文本和交互信息不应只通过 content 属性生成。

14.10 其他伪元素

css 复制代码
::selection {
  color: white;
  background: #2563eb;
}

input::placeholder {
  color: #94a3b8;
}

li::marker {
  color: #2563eb;
}

15. 变换、过渡与动画

15.1 transform

css 复制代码
.card {
  transform: translateY(-4px);
}

常用变换函数:

css 复制代码
.example {
  transform:
    translateX(20px)
    translateY(-10px)
    rotate(5deg)
    scale(1.05);
}

变换通常不会像修改普通布局尺寸那样重新安排周围元素的位置。

15.2 变换原点

css 复制代码
.element {
  transform-origin: center bottom;
}

15.3 过渡

css 复制代码
.button {
  color: white;
  background-color: #2563eb;
  transform: translateY(0);
  transition:
    background-color 180ms ease,
    transform 180ms ease;
}

.button:hover {
  background-color: #1d4ed8;
  transform: translateY(-2px);
}

不建议随意使用:

css 复制代码
transition: all 0.3s;

它可能让本来不需要动画的属性也参与过渡,增加意外行为和性能成本。

15.4 关键帧动画

css 复制代码
@keyframes pulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 1;
  }

  50% {
    transform: scale(1.08);
    opacity: 0.7;
  }
}

.notification-dot {
  animation: pulse 1.5s ease-in-out infinite;
}

15.5 加载动画

css 复制代码
@keyframes spin {
  to {
    transform: rotate(1turn);
  }
}

.spinner {
  width: 36px;
  aspect-ratio: 1;
  border: 4px solid #cbd5e1;
  border-top-color: #2563eb;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

HTML:

html 复制代码
<div class="spinner" role="status" aria-label="正在加载"></div>

15.6 动画填充和播放状态

css 复制代码
.element {
  animation-name: fade-in;
  animation-duration: 500ms;
  animation-timing-function: ease;
  animation-delay: 100ms;
  animation-iteration-count: 1;
  animation-direction: normal;
  animation-fill-mode: both;
  animation-play-state: running;
}

15.7 性能较友好的动画属性

通常优先考虑:

  • transform
  • opacity

频繁动画以下属性可能引起更多布局或绘制工作:

  • width
  • height
  • top
  • left
  • margin
  • box-shadow
  • filter

这不是绝对禁令,最终仍应通过浏览器性能工具测量。

15.8 will-change

css 复制代码
.dragging-element {
  will-change: transform;
}

will-change 只应在确定即将频繁变化时谨慎使用。长期给大量元素设置可能增加内存和合成成本。


16. CSS 变量与计算函数

16.1 定义变量

CSS 自定义属性通常定义在 :root

css 复制代码
:root {
  --color-primary: #2563eb;
  --color-text: #0f172a;
  --color-surface: #ffffff;
  --radius-md: 12px;
  --space-md: 16px;
}

使用变量:

css 复制代码
.button {
  padding: var(--space-md);
  color: white;
  background-color: var(--color-primary);
  border-radius: var(--radius-md);
}

16.2 变量回退值

css 复制代码
.card {
  color: var(--card-color, #334155);
}

如果 --card-color 没有定义,则使用 #334155

16.3 变量具有层叠和继承能力

css 复制代码
.theme-dark {
  --color-text: #e2e8f0;
  --color-surface: #0f172a;
}

.card {
  color: var(--color-text);
  background: var(--color-surface);
}

放在 .theme-dark 内的卡片会继承新的变量值。

16.4 组件局部变量

css 复制代码
.button {
  --button-bg: #2563eb;
  --button-color: white;

  color: var(--button-color);
  background: var(--button-bg);
}

.button-danger {
  --button-bg: #dc2626;
}

这种方式能减少重复代码。

16.5 calc()

css 复制代码
.sidebar {
  height: calc(100dvh - 64px);
}

.main {
  width: calc(100% - 280px);
}

16.6 min()

css 复制代码
.container {
  width: min(100% - 32px, 1200px);
}

浏览器会选择较小值。

16.7 max()

css 复制代码
.page {
  padding-inline: max(16px, 4vw);
}

浏览器会选择较大值。

16.8 clamp()

css 复制代码
.title {
  font-size: clamp(2rem, 6vw, 5rem);
}

结构:

text 复制代码
clamp(最小值, 理想值, 最大值)

16.9 env()

在需要适配安全区域的移动端页面中:

css 复制代码
.bottom-bar {
  padding-bottom: calc(12px + env(safe-area-inset-bottom));
}

17. 现代 CSS 进阶特性

17.1 层叠层 @layer

CSS Cascade Level 5 引入了层叠层,用于明确不同类型样式之间的覆盖顺序。

css 复制代码
@layer reset, base, components, utilities;

定义样式:

css 复制代码
@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  body {
    margin: 0;
  }
}

@layer base {
  body {
    color: #0f172a;
    font-family: system-ui, sans-serif;
  }
}

@layer components {
  .button {
    padding: 10px 18px;
    border-radius: 8px;
  }
}

@layer utilities {
  .text-center {
    text-align: center;
  }
}

对于普通声明,后声明的层通常具有更高的层级优先顺序:

text 复制代码
reset < base < components < utilities

使用 @layer 的优势:

  • 减少优先级竞争
  • 明确第三方样式、基础样式和业务样式的关系
  • 降低对 !important 的依赖
  • 让大型项目的覆盖规则更可预测

没有放入层叠层的普通作者样式会参与更高的未分层位置,因此项目应采用一致的层级策略。

17.2 原生 CSS 嵌套

现代 CSS 可以直接嵌套相关规则。语法由 W3C CSS Nesting Module Level 1 定义。

css 复制代码
.card {
  padding: 24px;
  border: 1px solid #e2e8f0;
  border-radius: 16px;

  & .card-title {
    margin: 0;
    color: #0f172a;
  }

  &:hover {
    border-color: #2563eb;
  }

  &.featured {
    background: #eff6ff;
  }

  @media (min-width: 48rem) {
    padding: 32px;
  }
}

嵌套不应该无限加深。推荐控制在较少层级,否则会产生复杂选择器和较高耦合。

17.3 @supports

根据浏览器是否支持某个 CSS 能力启用增强样式:

css 复制代码
.card-list {
  display: flex;
  flex-wrap: wrap;
}

@supports (display: grid) {
  .card-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
  }
}

检测选择器:

css 复制代码
@supports selector(:has(*)) {
  .card:has(.badge) {
    border-color: #2563eb;
  }
}

这种做法体现了渐进增强:

  1. 先提供可用的基础样式
  2. 浏览器支持新能力时再增强体验

17.4 aspect-ratio

css 复制代码
.video {
  width: 100%;
  aspect-ratio: 16 / 9;
}

.avatar {
  width: 64px;
  aspect-ratio: 1;
  border-radius: 50%;
}

17.5 accent-color

css 复制代码
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
  accent-color: #2563eb;
}

17.6 color-scheme

css 复制代码
:root {
  color-scheme: light dark;
}

这会告诉浏览器页面支持的配色方案,使部分表单控件、滚动条和浏览器默认颜色更好地匹配主题。

如果页面没有完整的深色主题,不要仅添加这一行就认为深色模式已经完成。

17.7 @property

@property 可以注册具有类型、初始值和继承行为的自定义属性:

css 复制代码
@property --progress {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

.progress {
  --progress: 0%;

  background:
    conic-gradient(
      #2563eb var(--progress),
      #e2e8f0 0
    );
  transition: --progress 500ms ease;
}

.progress:hover {
  --progress: 75%;
}

普通自定义属性通常按字符串标记处理,而注册后的自定义属性可以获得更明确的类型和插值行为。

17.8 content-visibility

对于较长页面中的独立区域:

css 复制代码
.article-section {
  content-visibility: auto;
  contain-intrinsic-size: auto 600px;
}

浏览器可以延迟处理屏幕外内容的部分渲染工作。contain-intrinsic-size 可以提供估算尺寸,减少滚动时的布局跳动。

不要盲目应用到所有元素,应通过性能工具确认它是否真正改善了目标页面。


18. CSS 组件设计

18.1 组件基础结构

HTML:

html 复制代码
<article class="product-card">
  <img
    class="product-card__image"
    src="./images/product.jpg"
    alt="机械键盘"
  >

  <div class="product-card__body">
    <h2 class="product-card__title">机械键盘</h2>
    <p class="product-card__description">
      支持多设备连接和热插拔轴体。
    </p>
    <button class="button button--primary">查看详情</button>
  </div>
</article>

CSS:

css 复制代码
.product-card {
  overflow: hidden;
  color: #0f172a;
  background: white;
  border: 1px solid #e2e8f0;
  border-radius: 16px;
  box-shadow: 0 10px 30px rgb(15 23 42 / 8%);
}

.product-card__image {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

.product-card__body {
  padding: 20px;
}

.product-card__title {
  margin: 0 0 8px;
  font-size: 1.25rem;
}

.product-card__description {
  margin: 0 0 20px;
  color: #64748b;
  line-height: 1.7;
}

18.2 组件变体

css 复制代码
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding-inline: 18px;
  border: 1px solid transparent;
  border-radius: 10px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.button--primary {
  color: white;
  background: #2563eb;
}

.button--outline {
  color: #2563eb;
  background: transparent;
  border-color: currentColor;
}

.button--danger {
  color: white;
  background: #dc2626;
}

18.3 用属性表达状态

HTML:

html 复制代码
<button
  class="button"
  type="button"
  data-variant="primary"
  data-loading="false"
>
  保存
</button>

CSS:

css 复制代码
.button[data-variant="primary"] {
  color: white;
  background: #2563eb;
}

.button[data-loading="true"] {
  cursor: wait;
  opacity: 0.7;
}

真实交互状态还应同步使用正确的 HTML 属性,例如:

html 复制代码
<button type="button" disabled aria-busy="true">
  正在保存
</button>

不要只依靠自定义属性模拟原生语义。

18.4 组件设计原则

一个可维护的 CSS 组件应该:

  • 具有明确边界
  • 尽量减少对父级结构的依赖
  • 使用低优先级类选择器
  • 允许通过变量定制主题
  • 明确定义尺寸和状态变体
  • 保留键盘焦点样式
  • 不依靠固定文案长度
  • 能适应不同容器宽度
  • 避免内部样式泄漏到其他组件

19. CSS 工程化与代码组织

19.1 推荐目录结构

text 复制代码
styles/
├── reset.css
├── tokens.css
├── base.css
├── layout.css
├── utilities.css
├── components/
│   ├── button.css
│   ├── card.css
│   ├── modal.css
│   └── form.css
└── pages/
    ├── home.css
    └── profile.css

19.2 设计令牌

css 复制代码
:root {
  /* 颜色 */
  --color-primary-500: #3b82f6;
  --color-primary-600: #2563eb;
  --color-slate-50: #f8fafc;
  --color-slate-900: #0f172a;

  /* 间距 */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  /* 圆角 */
  --radius-sm: 0.375rem;
  --radius-md: 0.75rem;
  --radius-lg: 1rem;

  /* 阴影 */
  --shadow-sm: 0 1px 2px rgb(15 23 42 / 6%);
  --shadow-md: 0 12px 30px rgb(15 23 42 / 10%);
}

设计令牌可以让颜色、间距和圆角保持统一。

19.3 BEM 命名

BEM 的基本形式:

text 复制代码
block__element--modifier

示例:

css 复制代码
.card {
}

.card__title {
}

.card__description {
}

.card--featured {
}

BEM 不是必须采用的标准,但其"组件、子元素、变体"思想有助于减少命名冲突。

19.4 工具类

css 复制代码
.text-center {
  text-align: center;
}

.mt-4 {
  margin-top: 1rem;
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

工具类适合表达单一、稳定的样式职责,但不要同时建立多套相互冲突的命名体系。

19.5 CSS Modules

组件化项目可以使用 CSS Modules:

css 复制代码
/* Button.module.css */
.button {
  padding: 10px 18px;
  border-radius: 8px;
}

在 JavaScript 或 TypeScript 中:

js 复制代码
import styles from "./Button.module.css";

export function Button() {
  return <button className={styles.button}>提交</button>;
}

构建工具会把类名转换为局部作用域名称,降低全局命名冲突。

19.6 Sass 等预处理器

Sass 可以提供:

  • 变量
  • 嵌套
  • 混入
  • 函数
  • 模块化
  • 循环和条件

不过现代 CSS 已经原生支持变量、嵌套和大量计算能力。是否引入预处理器,应根据项目实际复杂度决定。

19.7 PostCSS

PostCSS 是处理 CSS 的工具平台,常见用途包括:

  • 自动添加部分兼容前缀
  • 转换部分新语法
  • 压缩 CSS
  • 检查代码
  • 配合构建工具处理资源

不要手工维护大量浏览器前缀,应根据项目的浏览器支持范围交给工具生成。

19.8 避免过度抽象

下面这种命名过度依赖具体视觉值:

css 复制代码
.margin-top-17 {
  margin-top: 17px;
}

.blue-text-2 {
  color: #2878ff;
}

如果项目采用工具类体系,应使用统一的设计尺度;如果采用语义组件体系,应使用组件职责命名。两种方式都比临时数字类名更容易维护。


20. CSS 性能优化

20.1 删除无用 CSS

大型项目经过长期迭代后可能积累大量废弃规则。可以通过以下方式治理:

  • 分析构建产物
  • 按页面或组件拆分样式
  • 删除已下线组件的 CSS
  • 配置静态检查
  • 检查动态生成类名,避免误删
  • 定期审查第三方样式依赖

20.2 减少阻塞资源

CSS 通常会影响首次渲染。可以考虑:

  • 压缩生产环境 CSS
  • 避免加载不需要的完整组件库样式
  • 拆分页面级样式
  • 对静态资源启用缓存
  • 减少连续的 @import 网络依赖
  • 避免超大的内联样式

20.3 优化字体

css 复制代码
@font-face {
  font-family: "MyFont";
  src: url("./fonts/my-font.woff2") format("woff2");
  font-display: swap;
}

建议:

  • 优先使用 WOFF2
  • 只加载实际需要的字重
  • 谨慎加载多套字体
  • 合理使用 font-display
  • 根据项目情况进行字体子集化

20.4 避免布局抖动

图片应提供尺寸或比例:

css 复制代码
.article-cover {
  width: 100%;
  aspect-ratio: 16 / 9;
}

HTML 也可以直接声明:

html 复制代码
<img
  src="./cover.jpg"
  alt="文章封面"
  width="1600"
  height="900"
>

这样浏览器在图片加载前就能预留空间。

20.5 减少复杂动画

不要让大量元素持续执行高成本动画:

css 复制代码
/* 应谨慎评估 */
.large-list-item {
  animation: complex-shadow 1s infinite;
}

动画优化原则:

  • 只在需要时执行
  • 优先使用 transformopacity
  • 页面不可见时停止无意义动画
  • 尊重 prefers-reduced-motion
  • 使用性能面板测量,而不是只凭感觉判断

20.6 控制选择器复杂度

不推荐:

css 复制代码
.page .main .content .article ul li a span {
  color: blue;
}

更推荐:

css 复制代码
.article-link-label {
  color: blue;
}

现代浏览器匹配常见选择器通常很快,简化选择器的首要收益是降低耦合和提高可维护性。


21. CSS 与无障碍设计

21.1 保留清晰焦点

css 复制代码
:focus-visible {
  outline: 3px solid #2563eb;
  outline-offset: 3px;
}

21.2 保证足够的颜色对比度

不要使用过浅的文字:

css 复制代码
/* 可能难以阅读 */
.text {
  color: #d1d5db;
  background: white;
}

正文、按钮、链接、错误信息都需要在实际背景下检查对比度。

21.3 不要只通过颜色表达状态

不完整的错误提示:

css 复制代码
.input-error {
  border-color: red;
}

更好的做法:

html 复制代码
<label for="email">邮箱</label>
<input
  id="email"
  type="email"
  aria-invalid="true"
  aria-describedby="email-error"
>
<p id="email-error" class="error-message">
  邮箱格式不正确,请检查后重新输入。
</p>

CSS:

css 复制代码
.error-message {
  display: flex;
  gap: 8px;
  color: #b91c1c;
  font-weight: 600;
}

.error-message::before {
  content: "!";
  display: grid;
  width: 20px;
  aspect-ratio: 1;
  place-items: center;
  border: 2px solid currentColor;
  border-radius: 50%;
}

这里同时使用了颜色、图形和文字说明。

21.4 触摸目标尺寸

按钮和可点击区域应具有足够尺寸:

css 复制代码
.icon-button {
  display: inline-grid;
  width: 44px;
  height: 44px;
  place-items: center;
}

21.5 不要通过 CSS 重新排列语义

Grid 和 Flexbox 可以改变视觉顺序,但通常不会同步改变 DOM 阅读顺序和键盘焦点顺序。

HTML 应先按照合理的阅读和操作顺序编写,再使用 CSS 进行视觉布局。

21.6 动画可访问性

css 复制代码
@media (prefers-reduced-motion: reduce) {
  .animated-banner {
    animation: none;
    transition: none;
  }
}

如果动画承载业务含义,还应提供无需动画也能理解信息的方式。


22. 浏览器调试技巧

22.1 检查元素

打开浏览器开发者工具后,可以查看:

  • 元素匹配了哪些 CSS 规则
  • 哪些属性被覆盖
  • 属性来自哪个文件和行号
  • 最终计算样式
  • 盒模型尺寸
  • Flexbox 和 Grid 布局
  • 伪类状态
  • CSS 变量最终值

22.2 查找样式为什么没有生效

依次检查:

  1. CSS 文件是否成功加载
  2. 选择器是否匹配目标元素
  3. 属性名和值是否合法
  4. 声明是否被划掉
  5. 是否被更高层级规则覆盖
  6. 父元素是否限制了尺寸
  7. 当前元素是否处于预期布局模式
  8. 媒体查询或容器查询是否匹配
  9. 自定义属性是否定义
  10. 属性是否适用于该元素

22.3 检查盒模型

开发者工具中的盒模型可以查看:

text 复制代码
content
padding
border
margin

如果尺寸不符合预期,重点检查:

  • box-sizing
  • widthmax-width
  • min-width
  • 内外边距
  • 边框
  • Flex 或 Grid 的尺寸分配
  • 文本或图片是否撑开容器

22.4 强制伪类状态

开发者工具通常可以强制:

  • :hover
  • :active
  • :focus
  • :focus-visible
  • :visited

这对于调试按钮、菜单和表单非常方便。

22.5 临时添加轮廓

css 复制代码
* {
  outline: 1px solid rgb(255 0 0 / 15%);
}

它可以帮助快速观察元素边界,但不要提交到生产环境。

更精确的调试方式:

css 复制代码
.debug {
  outline: 2px solid red;
  background: rgb(255 0 0 / 8%);
}

22.6 检查横向滚动

页面出现横向滚动时,可以临时使用:

css 复制代码
* {
  outline: 1px solid red;
}

重点检查:

  • 固定宽度元素
  • width: 100vw
  • 长文本和长链接
  • 绝对定位元素
  • 负外边距
  • Grid 自动最小尺寸
  • 图片缺少 max-width: 100%
  • Flex 项目缺少 min-width: 0

不要直接用下面的代码掩盖问题:

css 复制代码
body {
  overflow-x: hidden;
}

应该先找到真正产生溢出的元素。


23. 完整响应式页面实战

下面实现一个不依赖 JavaScript 的响应式技术产品首页,综合使用:

  • CSS 变量
  • 层叠层
  • Flexbox
  • Grid
  • 响应式设计
  • 容器查询
  • 伪类
  • 动画
  • 深色模式
  • 无障碍焦点
  • 减少动画偏好

23.1 项目结构

text 复制代码
css-demo/
├── index.html
└── style.css

23.2 index.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta
    name="viewport"
    content="width=device-width, initial-scale=1"
  >
  <meta
    name="description"
    content="一个使用现代 CSS 构建的响应式页面示例"
  >
  <title>CSS 实战页面</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <a class="skip-link" href="#main-content">
    跳转到主要内容
  </a>

  <header class="site-header">
    <div class="container site-header__inner">
      <a class="brand" href="#" aria-label="CodeStyle 首页">
        <span class="brand__icon" aria-hidden="true">C</span>
        <span>CodeStyle</span>
      </a>

      <nav class="navigation" aria-label="主导航">
        <a class="navigation__link navigation__link--active" href="#">
          首页
        </a>
        <a class="navigation__link" href="#features">功能</a>
        <a class="navigation__link" href="#stats">数据</a>
        <a class="navigation__link" href="#contact">联系</a>
      </nav>

      <a class="button button--small button--outline" href="#contact">
        开始使用
      </a>
    </div>
  </header>

  <main id="main-content">
    <section class="hero">
      <div class="container hero__layout">
        <div class="hero__content">
          <p class="eyebrow">
            现代 CSS 学习示例
          </p>

          <h1 class="hero__title">
            使用 CSS 构建漂亮、快速且响应式的网页
          </h1>

          <p class="hero__description">
            掌握层叠、Flexbox、Grid、容器查询和设计令牌,
            建立真正可维护的页面样式体系。
          </p>

          <div class="hero__actions">
            <a class="button button--primary" href="#features">
              查看功能
            </a>
            <a class="button button--ghost" href="#contact">
              联系我们
            </a>
          </div>

          <ul class="hero__advantages" aria-label="产品优势">
            <li>响应式布局</li>
            <li>无障碍设计</li>
            <li>现代浏览器能力</li>
          </ul>
        </div>

        <div class="hero__visual" aria-hidden="true">
          <div class="code-window">
            <div class="code-window__header">
              <span class="code-window__dot"></span>
              <span class="code-window__dot"></span>
              <span class="code-window__dot"></span>
            </div>

            <pre class="code-window__content"><code><span class="code-selector">.page</span> {
  <span class="code-property">display</span>: grid;
  <span class="code-property">gap</span>: 2rem;
  <span class="code-property">color</span>: <span class="code-value">#2563eb</span>;
}</code></pre>
          </div>
        </div>
      </div>
    </section>

    <section id="features" class="section feature-section">
      <div class="container">
        <div class="section-heading">
          <p class="eyebrow">核心能力</p>
          <h2 class="section-heading__title">
            从基础样式到现代布局
          </h2>
          <p class="section-heading__description">
            每一个模块都可以独立复用,并根据容器尺寸自动调整。
          </p>
        </div>

        <div class="feature-grid">
          <article class="feature-card">
            <span class="feature-card__icon" aria-hidden="true">01</span>
            <h3 class="feature-card__title">清晰的层叠关系</h3>
            <p class="feature-card__description">
              使用层叠层、低优先级选择器和设计令牌,
              避免样式覆盖失控。
            </p>
            <a class="text-link" href="#">了解层叠规则</a>
          </article>

          <article class="feature-card">
            <span class="feature-card__icon" aria-hidden="true">02</span>
            <h3 class="feature-card__title">灵活的页面布局</h3>
            <p class="feature-card__description">
              组合使用 Flexbox 与 Grid,适配从手机到桌面的各种尺寸。
            </p>
            <a class="text-link" href="#">了解布局方式</a>
          </article>

          <article class="feature-card">
            <span class="feature-card__icon" aria-hidden="true">03</span>
            <h3 class="feature-card__title">独立的响应式组件</h3>
            <p class="feature-card__description">
              使用容器查询,让组件根据实际可用空间改变内部布局。
            </p>
            <a class="text-link" href="#">了解容器查询</a>
          </article>
        </div>
      </div>
    </section>

    <section id="stats" class="section section--muted">
      <div class="container stats-grid">
        <article class="stat-card">
          <strong class="stat-card__value">12+</strong>
          <span class="stat-card__label">核心章节</span>
        </article>

        <article class="stat-card">
          <strong class="stat-card__value">30+</strong>
          <span class="stat-card__label">代码示例</span>
        </article>

        <article class="stat-card">
          <strong class="stat-card__value">100%</strong>
          <span class="stat-card__label">响应式布局</span>
        </article>

        <article class="stat-card">
          <strong class="stat-card__value">0</strong>
          <span class="stat-card__label">JavaScript 依赖</span>
        </article>
      </div>
    </section>

    <section id="contact" class="section">
      <div class="container">
        <div class="contact-card">
          <div>
            <p class="eyebrow">开始实践</p>
            <h2 class="contact-card__title">
              从一个小组件开始学习 CSS
            </h2>
            <p class="contact-card__description">
              先实现结构,再完成基础样式,最后逐步增加响应式和动画。
            </p>
          </div>

          <a class="button button--primary" href="mailto:hello@example.com">
            立即联系
          </a>
        </div>
      </div>
    </section>
  </main>

  <footer class="site-footer">
    <div class="container site-footer__inner">
      <p>© 2026 CodeStyle. CSS 学习示例。</p>
      <a href="#">返回顶部</a>
    </div>
  </footer>
</body>
</html>

23.3 style.css

css 复制代码
@layer reset, tokens, base, layout, components, utilities;

@layer reset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  html {
    scroll-behavior: smooth;
  }

  body,
  h1,
  h2,
  h3,
  p,
  ul {
    margin: 0;
  }

  ul {
    padding: 0;
  }

  img,
  svg {
    display: block;
    max-width: 100%;
  }

  button,
  input,
  textarea,
  select {
    font: inherit;
  }
}

@layer tokens {
  :root {
    color-scheme: light;

    --color-primary: #2563eb;
    --color-primary-dark: #1d4ed8;
    --color-secondary: #7c3aed;
    --color-text: #0f172a;
    --color-text-muted: #64748b;
    --color-page: #ffffff;
    --color-surface: #ffffff;
    --color-surface-muted: #f8fafc;
    --color-border: #e2e8f0;

    --container-width: 72rem;

    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-10: 2.5rem;
    --space-12: 3rem;
    --space-16: 4rem;
    --space-20: 5rem;

    --radius-sm: 0.5rem;
    --radius-md: 0.75rem;
    --radius-lg: 1rem;
    --radius-xl: 1.5rem;

    --shadow-sm: 0 1px 3px rgb(15 23 42 / 8%);
    --shadow-md: 0 16px 40px rgb(15 23 42 / 10%);
  }

  @media (prefers-color-scheme: dark) {
    :root {
      color-scheme: dark;

      --color-text: #e2e8f0;
      --color-text-muted: #94a3b8;
      --color-page: #020617;
      --color-surface: #0f172a;
      --color-surface-muted: #111c30;
      --color-border: #263449;
      --shadow-sm: 0 1px 3px rgb(0 0 0 / 30%);
      --shadow-md: 0 16px 40px rgb(0 0 0 / 35%);
    }
  }
}

@layer base {
  body {
    min-width: 20rem;
    color: var(--color-text);
    background: var(--color-page);
    font-family:
      Inter,
      "PingFang SC",
      "Microsoft YaHei",
      system-ui,
      -apple-system,
      BlinkMacSystemFont,
      "Segoe UI",
      sans-serif;
    line-height: 1.6;
  }

  a {
    color: inherit;
    text-decoration: none;
  }

  :focus-visible {
    outline: 3px solid rgb(37 99 235 / 40%);
    outline-offset: 3px;
  }
}

@layer layout {
  .container {
    width: min(100% - 2rem, var(--container-width));
    margin-inline: auto;
  }

  .section {
    padding-block: clamp(4rem, 8vw, 7rem);
  }

  .section--muted {
    background: var(--color-surface-muted);
  }

  .site-header__inner,
  .site-footer__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
  }
}

@layer components {
  .skip-link {
    position: fixed;
    z-index: 1000;
    top: var(--space-4);
    left: var(--space-4);
    padding: var(--space-3) var(--space-4);
    color: white;
    background: var(--color-primary);
    border-radius: var(--radius-sm);
    transform: translateY(-200%);
  }

  .skip-link:focus {
    transform: translateY(0);
  }

  .site-header {
    position: sticky;
    z-index: 20;
    top: 0;
    border-bottom: 1px solid var(--color-border);
    background: color-mix(
      in srgb,
      var(--color-page) 85%,
      transparent
    );
    backdrop-filter: blur(14px);
  }

  .site-header__inner {
    min-height: 4.5rem;
  }

  .brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    font-size: 1.125rem;
    font-weight: 800;
  }

  .brand__icon {
    display: grid;
    width: 2.25rem;
    aspect-ratio: 1;
    place-items: center;
    color: white;
    background:
      linear-gradient(
        135deg,
        var(--color-primary),
        var(--color-secondary)
      );
    border-radius: var(--radius-sm);
  }

  .navigation {
    display: none;
    align-items: center;
    gap: var(--space-2);
  }

  .navigation__link {
    padding: var(--space-2) var(--space-3);
    color: var(--color-text-muted);
    border-radius: var(--radius-sm);
    transition:
      color 160ms ease,
      background-color 160ms ease;
  }

  .navigation__link:hover,
  .navigation__link--active {
    color: var(--color-primary);
    background: rgb(37 99 235 / 8%);
  }

  .button {
    display: inline-flex;
    min-height: 2.75rem;
    align-items: center;
    justify-content: center;
    padding-inline: 1.25rem;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-weight: 700;
    text-align: center;
    cursor: pointer;
    transition:
      color 160ms ease,
      background-color 160ms ease,
      border-color 160ms ease,
      transform 160ms ease;
  }

  .button:hover {
    transform: translateY(-2px);
  }

  .button--small {
    min-height: 2.5rem;
    padding-inline: 1rem;
    font-size: 0.875rem;
  }

  .button--primary {
    color: white;
    background: var(--color-primary);
    box-shadow: 0 10px 24px rgb(37 99 235 / 25%);
  }

  .button--primary:hover {
    background: var(--color-primary-dark);
  }

  .button--outline {
    color: var(--color-primary);
    border-color: currentColor;
  }

  .button--ghost {
    color: var(--color-text);
    background: var(--color-surface);
    border-color: var(--color-border);
  }

  .hero {
    position: relative;
    overflow: hidden;
    padding-block: clamp(4rem, 10vw, 8rem);
    background:
      radial-gradient(
        circle at 15% 10%,
        rgb(37 99 235 / 14%),
        transparent 32%
      ),
      radial-gradient(
        circle at 90% 80%,
        rgb(124 58 237 / 14%),
        transparent 30%
      );
  }

  .hero__layout {
    display: grid;
    align-items: center;
    gap: var(--space-12);
  }

  .hero__content {
    max-width: 42rem;
  }

  .eyebrow {
    margin-bottom: var(--space-4);
    color: var(--color-primary);
    font-size: 0.8125rem;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
  }

  .hero__title {
    max-width: 15ch;
    font-size: clamp(2.5rem, 7vw, 5rem);
    line-height: 1.08;
    letter-spacing: -0.04em;
  }

  .hero__description {
    max-width: 60ch;
    margin-top: var(--space-6);
    color: var(--color-text-muted);
    font-size: clamp(1rem, 2vw, 1.25rem);
  }

  .hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-top: var(--space-8);
  }

  .hero__advantages {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4) var(--space-6);
    margin-top: var(--space-8);
    color: var(--color-text-muted);
    list-style: none;
  }

  .hero__advantages li {
    display: flex;
    align-items: center;
    gap: var(--space-2);
  }

  .hero__advantages li::before {
    content: "✓";
    display: grid;
    width: 1.25rem;
    aspect-ratio: 1;
    place-items: center;
    color: white;
    background: #16a34a;
    border-radius: 50%;
    font-size: 0.75rem;
    font-weight: 900;
  }

  .hero__visual {
    min-width: 0;
  }

  .code-window {
    overflow: hidden;
    max-width: 34rem;
    margin-inline: auto;
    background: #0f172a;
    border: 1px solid rgb(255 255 255 / 12%);
    border-radius: var(--radius-xl);
    box-shadow: 0 30px 80px rgb(15 23 42 / 28%);
    transform: rotate(2deg);
  }

  .code-window__header {
    display: flex;
    gap: var(--space-2);
    padding: var(--space-4);
    border-bottom: 1px solid rgb(255 255 255 / 10%);
  }

  .code-window__dot {
    width: 0.75rem;
    aspect-ratio: 1;
    background: #fb7185;
    border-radius: 50%;
  }

  .code-window__dot:nth-child(2) {
    background: #fbbf24;
  }

  .code-window__dot:nth-child(3) {
    background: #4ade80;
  }

  .code-window__content {
    overflow: auto;
    margin: 0;
    padding: clamp(1.5rem, 5vw, 3rem);
    color: #e2e8f0;
    font: 600 clamp(0.9rem, 2vw, 1.15rem) / 1.9
      ui-monospace,
      SFMono-Regular,
      Menlo,
      Consolas,
      monospace;
  }

  .code-selector {
    color: #c084fc;
  }

  .code-property {
    color: #7dd3fc;
  }

  .code-value {
    color: #86efac;
  }

  .section-heading {
    max-width: 42rem;
    margin-bottom: var(--space-12);
  }

  .section-heading__title {
    font-size: clamp(2rem, 5vw, 3.25rem);
    line-height: 1.15;
    letter-spacing: -0.03em;
  }

  .section-heading__description {
    margin-top: var(--space-4);
    color: var(--color-text-muted);
    font-size: 1.125rem;
  }

  .feature-section {
    container: features / inline-size;
  }

  .feature-grid {
    display: grid;
    gap: var(--space-6);
  }

  .feature-card {
    display: grid;
    grid-template-rows: auto auto 1fr auto;
    gap: var(--space-4);
    padding: clamp(1.5rem, 4cqi, 2.25rem);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
    transition:
      border-color 180ms ease,
      box-shadow 180ms ease,
      transform 180ms ease;
  }

  .feature-card:hover {
    border-color: rgb(37 99 235 / 45%);
    box-shadow: var(--shadow-md);
    transform: translateY(-4px);
  }

  .feature-card__icon {
    display: grid;
    width: 3rem;
    aspect-ratio: 1;
    place-items: center;
    color: var(--color-primary);
    background: rgb(37 99 235 / 10%);
    border-radius: var(--radius-md);
    font-weight: 900;
  }

  .feature-card__title {
    font-size: 1.25rem;
  }

  .feature-card__description {
    color: var(--color-text-muted);
  }

  .text-link {
    width: fit-content;
    color: var(--color-primary);
    font-weight: 700;
  }

  .text-link::after {
    content: " →";
  }

  .stats-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
  }

  .stat-card {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-6);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
  }

  .stat-card__value {
    color: var(--color-primary);
    font-size: clamp(2rem, 5vw, 3.5rem);
    line-height: 1;
  }

  .stat-card__label {
    color: var(--color-text-muted);
  }

  .contact-card {
    display: grid;
    align-items: center;
    gap: var(--space-8);
    padding: clamp(2rem, 6vw, 4rem);
    color: white;
    background:
      linear-gradient(
        135deg,
        var(--color-primary),
        var(--color-secondary)
      );
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-md);
  }

  .contact-card .eyebrow {
    color: rgb(255 255 255 / 75%);
  }

  .contact-card__title {
    max-width: 20ch;
    font-size: clamp(1.75rem, 4vw, 3rem);
    line-height: 1.15;
  }

  .contact-card__description {
    max-width: 55ch;
    margin-top: var(--space-4);
    color: rgb(255 255 255 / 82%);
  }

  .contact-card .button--primary {
    color: var(--color-primary);
    background: white;
    box-shadow: none;
  }

  .site-footer {
    padding-block: var(--space-8);
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
  }

  .site-footer__inner {
    flex-direction: column;
    text-align: center;
  }

  .site-footer a {
    color: var(--color-primary);
    font-weight: 700;
  }

  @container features (min-width: 42rem) {
    .feature-grid {
      grid-template-columns: repeat(3, 1fr);
    }
  }

  @media (min-width: 48rem) {
    .navigation {
      display: flex;
    }

    .stats-grid {
      grid-template-columns: repeat(4, 1fr);
    }

    .contact-card {
      grid-template-columns: minmax(0, 1fr) auto;
    }

    .site-footer__inner {
      flex-direction: row;
      text-align: start;
    }
  }

  @media (min-width: 64rem) {
    .hero__layout {
      grid-template-columns: minmax(0, 1.1fr) minmax(24rem, 0.9fr);
    }
  }
}

@layer utilities {
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    white-space: nowrap;
    border: 0;
    clip-path: inset(50%);
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

23.4 实战项目知识点

这个页面包含以下设计思想:

  1. 通过 @layer 管理样式覆盖顺序
  2. 通过 CSS 变量统一颜色、间距、圆角和阴影
  3. 使用移动优先的媒体查询
  4. 使用 Grid 完成主要二维布局
  5. 使用 Flexbox 完成导航和按钮组布局
  6. 使用容器查询控制卡片布局
  7. 使用 clamp() 实现流式字号
  8. 使用逻辑属性提高国际化适配能力
  9. 使用 prefers-color-scheme 提供深色模式
  10. 使用 prefers-reduced-motion 尊重用户动画偏好
  11. 使用 :focus-visible 保留键盘焦点
  12. 使用跳转链接帮助键盘用户快速进入主要内容

24. 常见问题与面试知识

24.1 为什么 height: 100% 没有效果

百分比高度通常需要父元素具有可确定的高度:

css 复制代码
html,
body {
  height: 100%;
}

.page {
  min-height: 100%;
}

如果目标是页面至少占满视口,可以使用:

css 复制代码
.page {
  min-height: 100dvh;
}

24.2 为什么文字省略号没有出现

需要满足:

css 复制代码
.text {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

并且元素需要受到宽度约束。

在 Flexbox 中可能还需要:

css 复制代码
.flex-item {
  min-width: 0;
}

24.3 为什么 z-index 没有效果

检查:

  • 元素是否位于不同层叠上下文
  • 祖先是否设置了 transform
  • 祖先是否设置了 opacity
  • 是否存在 isolation
  • 元素的定位方式
  • 比较的是不是同一个层叠上下文内的元素

24.4 为什么 position: sticky 失效

常见原因:

  • 没有设置 top 或其他边界
  • 父容器没有足够滚动距离
  • 祖先的 overflow 创建了不同滚动容器
  • 元素高度过大
  • 实际滚动的不是预期容器

24.5 display: nonevisibility: hiddenopacity: 0 的区别

属性 是否占据空间 是否可见 是否可能继续交互
display: none 通常否
visibility: hidden 通常否
opacity: 0

24.6 emrem 的区别

  • rem 相对于根元素字体大小
  • em 根据使用属性的上下文,通常与当前元素字体大小有关
  • font-size 上使用 em 时,会根据父元素的字体大小计算
  • 多层嵌套使用 em 可能出现累积放大

24.7 Flexbox 和 Grid 的区别

  • Flexbox 主要处理一维排列
  • Grid 主要处理二维网格
  • Flexbox 更关注内容分配
  • Grid 更关注行列轨道
  • 实际项目中可以组合使用

24.8 什么是 BFC

BFC 可以理解为块级格式化上下文,是一个相对独立的布局区域。

常见创建方式包括:

css 复制代码
.element {
  display: flow-root;
}

其他布局或特定属性也可能创建 BFC,例如:

  • display: flex
  • display: grid
  • 绝对定位
  • 浮动
  • 某些非 visibleoverflow

display: flow-root 是为了创建独立块级格式化上下文时较清晰的写法:

css 复制代码
.container {
  display: flow-root;
}

24.9 如何清除浮动

旧式方法:

css 复制代码
.clearfix::after {
  content: "";
  display: table;
  clear: both;
}

现代写法:

css 复制代码
.container {
  display: flow-root;
}

新项目的主要布局通常应该优先考虑 Flexbox 或 Grid,而不是依靠浮动。

24.10 如何实现水平垂直居中

Flexbox:

css 复制代码
.parent {
  display: flex;
  align-items: center;
  justify-content: center;
}

Grid:

css 复制代码
.parent {
  display: grid;
  place-items: center;
}

绝对定位:

css 复制代码
.parent {
  position: relative;
}

.child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

优先选择符合整体布局需求的方式。

24.11 什么是重排和重绘

可以简化理解为:

  • 重排:布局信息发生变化,需要重新计算元素几何位置和尺寸
  • 重绘:视觉外观变化,需要重新绘制像素
  • 合成:浏览器把不同图层组合到最终画面

具体渲染过程由浏览器实现决定。性能优化时应使用开发者工具测量,不要只依靠固定规则猜测。

24.12 为什么不建议滥用 !important

因为它会:

  • 打乱正常层叠
  • 增加后续覆盖难度
  • 形成更多 !important
  • 让组件难以复用
  • 隐藏选择器设计问题

适合使用的场景通常非常有限,例如必须覆盖不可控第三方行内样式的特定补丁,或者明确设计为高优先级的少量工具规则。

24.13 常见 CSS 错误

错误一:固定宽度导致移动端溢出
css 复制代码
.container {
  width: 1200px;
}

改进:

css 复制代码
.container {
  width: min(100% - 32px, 1200px);
  margin-inline: auto;
}
错误二:过深选择器
css 复制代码
.page .main .article .header .title {
  color: blue;
}

改进:

css 复制代码
.article-title {
  color: blue;
}
错误三:使用定位完成普通布局
css 复制代码
.card {
  position: absolute;
  left: 320px;
  top: 180px;
}

普通页面布局应优先使用 Grid、Flexbox 和文档流。

错误四:删除焦点样式
css 复制代码
button:focus {
  outline: none;
}

改进:

css 复制代码
button:focus-visible {
  outline: 3px solid #2563eb;
  outline-offset: 3px;
}
错误五:动画所有属性
css 复制代码
.card {
  transition: all 0.5s;
}

改进:

css 复制代码
.card {
  transition:
    transform 180ms ease,
    box-shadow 180ms ease;
}

25. CSS 学习路线

25.1 第一阶段:基础语法

需要掌握:

  • CSS 引入方式
  • 选择器
  • 属性和值
  • 颜色和单位
  • 文本和字体
  • 背景和边框

练习目标:

  • 完成个人简介页
  • 完成文章详情页
  • 完成登录表单

25.2 第二阶段:盒模型与文档流

需要掌握:

  • 盒模型
  • box-sizing
  • 内外边距
  • 宽高约束
  • display
  • 文档流
  • 定位
  • 层叠上下文

练习目标:

  • 完成卡片组件
  • 完成固定导航栏
  • 完成带角标的商品卡片

25.3 第三阶段:现代布局

需要掌握:

  • Flexbox
  • Grid
  • gap
  • minmax()
  • repeat()
  • auto-fit
  • 对齐方式

练习目标:

  • 完成导航栏
  • 完成后台管理布局
  • 完成响应式卡片网格
  • 完成图片画廊

25.4 第四阶段:响应式设计

需要掌握:

  • 媒体查询
  • 移动优先
  • 相对单位
  • clamp()
  • 响应式图片
  • 逻辑属性
  • 容器查询

练习目标:

  • 将桌面页面适配到手机
  • 完成响应式博客首页
  • 完成可在侧边栏复用的卡片组件

25.5 第五阶段:交互与动画

需要掌握:

  • 伪类
  • 伪元素
  • transform
  • transition
  • animation
  • prefers-reduced-motion

练习目标:

  • 完成按钮状态
  • 完成下拉菜单
  • 完成加载动画
  • 完成卡片悬浮效果

25.6 第六阶段:工程化

需要掌握:

  • CSS 变量
  • 设计令牌
  • 组件命名
  • 层叠层
  • CSS Modules
  • Sass
  • PostCSS
  • 构建产物分析
  • 浏览器兼容策略

练习目标:

  • 建立一套按钮组件
  • 建立主题变量
  • 建立统一间距和颜色系统
  • 重构一个存在样式冲突的旧页面

25.7 第七阶段:性能和无障碍

需要掌握:

  • 焦点样式
  • 颜色对比度
  • 减少动画偏好
  • 字体加载
  • 布局稳定性
  • CSS 体积治理
  • 浏览器性能工具

练习目标:

  • 使用键盘完整操作页面
  • 检查深色模式
  • 检查页面横向溢出
  • 检查图片加载前后的布局变化
  • 分析并删除无用 CSS

26. 总结

CSS 的学习不应该停留在背诵属性。真正重要的是理解它背后的规则:

  1. 选择器决定样式匹配哪些元素
  2. 层叠决定冲突时哪个声明胜出
  3. 继承让部分样式沿元素树传播
  4. 盒模型决定元素的实际尺寸
  5. 文档流决定元素的基础排列方式
  6. Flexbox 适合一维布局
  7. Grid 适合二维布局
  8. 媒体查询适合页面级响应式设计
  9. 容器查询适合组件级响应式设计
  10. CSS 变量适合建立主题和设计令牌
  11. 层叠层可以降低大型项目的覆盖复杂度
  12. 性能、无障碍和可维护性同样属于 CSS 的核心能力

学习 CSS 最有效的方法是:

text 复制代码
理解规则 → 编写页面 → 使用开发者工具检查 → 找到原因 → 重构代码

当你能够解释"为什么这个样式会生效""为什么元素出现在这里""为什么页面发生溢出"时,就已经从记忆属性进入了真正掌握 CSS 的阶段。


27. 参考资料

相关推荐
SWAGGY..1 小时前
【C++初阶】:(15)模板进阶--从非类型参数到模板特化与分离编译
java·服务器·前端
YHHLAI1 小时前
从「跑分」到「干活」:Benchmark 与 GPT 5.6 的两种叙事
大数据·前端·人工智能·react.js·前端框架
BigTopOne1 小时前
ArLiveLite 用到的 C++/JNI 技术点
前端
BigTopOne1 小时前
ArLiveLite 技术架构-Lite
前端
BigTopOne1 小时前
ArLiveLite-具体功能
前端
BigTopOne1 小时前
ArLiveLite -具体功能
前端
Sherotree1 小时前
理解 JWT:三段分别是什么
前端·ai·开源
BigTopOne1 小时前
ArLiveLite 用到的 FFmpeg API
前端
前端技术官1 小时前
一行 CSS 新特性干掉 20 行 JavaScript ?
css·滚动驱动动画·容器查询·:has()·anchor positioning