承接第一篇,我们已经用 HTML5 搭好了 TaskFlow 任务管理器的骨架。本篇将系统讲解 CSS3 的全部知识点,并让骨架变成一个漂亮的界面。
目录
- [CSS 是什么 & 引入方式](#CSS 是什么 & 引入方式)
- 选择器全解
- 层叠、继承与优先级
- 盒模型
- 常用属性:字体/文本/背景/边框
- [display 与元素类型](#display 与元素类型)
- [定位 position 与层叠上下文](#定位 position 与层叠上下文)
- [Flexbox 弹性布局全解](#Flexbox 弹性布局全解)
- [Grid 网格布局全解](#Grid 网格布局全解)
- 过渡、变形、动画
- [CSS 变量与函数](#CSS 变量与函数)
- 媒体查询与响应式设计
- [现代 CSS 新特性](#现代 CSS 新特性)
- [项目实战:TaskFlow 完整样式](#项目实战:TaskFlow 完整样式)
- 本篇小结与练习
一、CSS 是什么 & 引入方式
CSS(Cascading Style Sheets,层叠样式表) 负责描述 HTML 的外观表现。
三种引入方式
html
<!-- 1. 行内样式(内联):优先级最高,但难维护,少用 -->
<p style="color: red; font-size: 16px;">文字</p>
<!-- 2. 内部样式表:写在 head 里的 <style> -->
<head>
<style>
p { color: red; }
</style>
</head>
<!-- 3. 外部样式表:最推荐,可缓存、可复用 -->
<link rel="stylesheet" href="./css/main.css">
<style> 里也能用 @import(不推荐,会阻塞渲染):
css
@import url('./variables.css');
CSS 规则的结构
css
/* 选择器 { 属性: 值; 属性: 值; } */
.selector {
color: #333; /* 声明 */
font-size: 16px; /* 声明 */
}
组成部分:
- 选择器(selector):选中目标元素
- 属性(property):要修改的特性
- 值(value):属性取值
- 声明(declaration) :
属性: 值 - 规则块(rule) :
{ }内的全部声明
注释
css
/* 单行注释 */
/* 多行注释
可以换行 */
/* 注意:CSS 没有 // 注释 */
二、选择器全解
2.1 基础选择器
css
* { } /* 通配符:所有元素 */
p { } /* 元素选择器 */
.class { } /* 类选择器,可多个元素共用 */
#id { } /* ID 选择器,页面唯一 */
2.2 组合选择器
css
/* 后代:div 内任意层级的 p */
div p { }
/* 直接子代:div 的直系子元素 p */
div > p { }
/* 相邻兄弟:紧跟在 h2 后的第一个 p */
h2 + p { }
/* 通用兄弟:h2 之后所有同级 p */
h2 ~ p { }
2.3 属性选择器
css
[disabled] { } /* 有该属性 */
[type="text"] { } /* 精确匹配 */
[class~="btn"] { } /* 空格分隔的列表中含 btn */
[lang|="zh"] { } /* 以 zh- 开头或等于 zh */
[href^="https"] { } /* 以 https 开头 */
[href$=".pdf"] { } /* 以 .pdf 结尾 */
[href*="example"] { } /* 包含 example */
[data-role="admin" i] { } /* 加 i 表示大小写不敏感 */
2.4 伪类选择器(:xxx)
交互状态类:
css
a:link { } /* 未访问链接 */
a:visited { } /* 已访问链接 */
a:hover { } /* 悬停 */
a:active { } /* 按下瞬间 */
input:focus { } /* 聚焦 */
input:focus-within { } /* 自身或子元素聚焦 */
LVHA 顺序:link → visited → hover → active,否则样式不生效。
结构伪类:
css
li:first-child { } /* 父元素第一个子元素 */
li:last-child { } /* 最后一个 */
li:only-child { } /* 唯一子元素 */
li:nth-child(3) { } /* 第 3 个 */
li:nth-child(odd) { } /* 奇数 1,3,5... */
li:nth-child(even) { } /* 偶数 2,4,6... */
li:nth-child(2n+1) { } /* 公式:偶数用 2n,前 3 个用 -n+3 */
li:nth-last-child(2) { } /* 倒数第 2 个 */
/* of-type 系列:只按同类型标签计数 */
p:first-of-type { }
p:last-of-type { }
p:nth-of-type(2) { }
/* 过滤空元素 */
div:empty { } /* 没有任何内容(包括空格) */
:nth-child 公式详解:
| 公式 | 含义 |
|---|---|
2n / even |
偶数位 |
2n+1 / odd |
奇数位 |
3n |
3 的倍数位 |
n+4 |
第 4 个开始之后 |
-n+3 |
前 3 个 |
2n+3 |
3,5,7,9... |
表单伪类:
css
input:checked { } /* 勾选的 checkbox/radio */
input:disabled { } /* 禁用 */
input:enabled { } /* 启用 */
input:required { } /* 必填 */
input:optional { } /* 非必填 */
input:valid { } /* 校验通过 */
input:invalid { } /* 校验失败 */
input:in-range { } /* 数值在范围 */
input:out-of-range { }
input:read-only { }
input:placeholder-shown { } /* 显示 placeholder 时(即空) */
其他逻辑伪类:
css
:not(.active) { } /* 排除 */
:is(h1, h2, h3) { } /* 匹配其中任意一个 */
:where(h1, h2, h3) { } /* 同 is,但优先级为 0 */
:has(> img) { } /* 包含某子元素(现代浏览器支持) */
:target { } /* URL 锚点指向的元素 */
:root { } /* 根元素 html,常用于定义变量 */
2.5 伪元素选择器(::xxx)
css
p::first-line { } /* 第一行 */
p::first-letter { } /* 第一个字母 */
p::selection { } /* 鼠标选中文本 */
p::before { content: "★"; } /* 元素内容前插入 */
p::after { content: "→"; } /* 元素内容后插入 */
input::placeholder { } /* 占位文字 */
li::marker { } /* 列表项符号 */
::before/::after必须写content,哪怕是content: "";。
2.6 选择器优先级(权值)
| 类型 | 权值 | 示例 |
|---|---|---|
| 行内样式 | 1000 | style="..." |
| ID 选择器 | 100 | #app |
| 类/属性/伪类 | 10 | .btn、[type]、:hover |
| 元素/伪元素 | 1 | div、::before |
| 通配符/组合器 | 0 | *、>、+ |
比较规则 :从左往右逐位比较,先大者赢;相同则后写的胜出(层叠)。
css
/* 权值 (0,2,0),比下面大 */
.btn.primary { color: red; }
/* 权值 (0,1,0) */
.btn { color: blue; }
!important :强制最高优先级,但会破坏层叠,慎用:
css
p { color: red !important; }
2.7 :is() / :where() / :has() 现代用法
css
/* 简化重复:以前要写三次 */
article h1, article h2, article h3 { }
/* 现在 */
article :is(h1, h2, h3) { }
/* :where 优先级为 0,方便被覆盖 */
:where(ul, ol) li { margin-left: 1rem; }
/* :has 父级选择:包含 img 的 card 加边框 */
.card:has(img) { border: 1px solid #ddd; }
三、层叠、继承与优先级
3.1 层叠(Cascade)
当多条规则作用同一元素时,按以下顺序决胜:
- 来源 :浏览器默认 < 用户样式 < 作者样式 < 作者
!important< 用户!important - 优先级(权值)
- 书写顺序:后者胜
3.2 继承(Inheritance)
部分属性会自动从父元素传给子元素,无需重复声明。
可继承属性:
- 字体:
font-family、font-size、font-weight、font-style - 文本:
color、line-height、text-align、letter-spacing、word-spacing - 列表:
list-style - 可见性:
visibility、cursor
不可继承(常见):
margin、padding、borderbackground、width、heightdisplay、position、float
强制继承 / 取消继承:
css
.child {
color: inherit; /* 强制继承父级 */
border: initial; /* 恢复为默认值 */
margin: unset; /* 可继承属性=inherit,其余=initial */
color: revert; /* 恢复为浏览器用户代理样式 */
}
技巧:整体统一字体大小用 rem + 根元素设置
css
html { font-size: 16px; }
p { font-size: 1rem; } /* 16px */
h1 { font-size: 2rem; } /* 32px */
四、盒模型
4.1 四层结构
┌─────────────────────────────────────┐
│ margin(外边距) │
│ ┌───────────────────────────────┐ │
│ │ border(边框) │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ padding(内边距) │ │ │
│ │ │ ┌───────────────────┐ │ │ │
│ │ │ │ content(内容) │ │ │ │
│ │ │ └───────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
4.2 box-sizing
css
/* 默认:width = content 宽,加上 padding/border 会更大 */
.box { box-sizing: content-box; width: 200px; padding: 20px; border: 5px; }
/* 实际占宽 = 200 + 40 + 10 = 250px */
/* 推荐:width 已含 padding + border */
.box { box-sizing: border-box; width: 200px; padding: 20px; border: 5px; }
/* 实际占宽 = 200px,内容区 = 200 - 40 - 10 = 150px */
项目全局重置 :
*, *::before, *::after { box-sizing: border-box; }是现代标配。
4.3 外边距塌陷(Margin Collapse)
三种塌陷场景:
html
<!-- 1. 相邻兄弟:上下边距合并,取较大者 -->
<p style="margin-bottom: 30px">A</p>
<p style="margin-top: 20px">B</p>
<!-- 实际间距 = 30px,不是 50px -->
<!-- 2. 父子:子元素 margin-top 会"顶"穿父元素 -->
<div class="parent">
<div class="child" style="margin-top: 20px">...</div>
</div>
<!-- parent 顶部也会出现 20px 的外间距 -->
<!-- 3. 空元素:自身上下边距合并 -->
<div style="margin: 20px 0"></div>
解决父子塌陷的方法:
css
.parent {
overflow: hidden; /* 创建 BFC */
/* 或者 */
padding-top: 1px;
/* 或者 */
border-top: 1px solid transparent;
/* 或者 */
display: flow-root; /* 现代最推荐 */
}
4.4 外边距 vs 内边距
| 属性 | 作用位置 | 背景色覆盖 | 常用于 |
|---|---|---|---|
margin |
元素外部 | 否 | 元素间距 |
padding |
元素内部 | 是 | 内容与边框距离 |
简写规则:
css
margin: 10px; /* 四周 10 */
margin: 10px 20px; /* 上下 10,左右 20 */
margin: 10px 20px 30px; /* 上 10,左右 20,下 30 */
margin: 10px 20px 30px 40px; /* 上 右 下 左(顺时针) */
margin: 0 auto; /* 块级元素水平居中 */
4.5 BFC(块级格式化上下文)
BFC 是一个独立的渲染区域,内部布局不影响外部。
触发方式:
overflow不为visibledisplay: flow-root/inline-block/flex/gridposition: absolute/fixedfloat不为none
应用:
- 清除浮动
- 阻止父子 margin 塌陷
- 阻止元素被浮动元素覆盖
css
/* 经典两栏布局,右侧不被左侧浮动覆盖 */
.left { float: left; width: 200px; }
.right { overflow: hidden; } /* 触发 BFC */
五、常用属性:字体/文本/背景/边框
5.1 字体
css
.text {
font-family: system-ui, -apple-system, "Segoe UI",
"PingFang SC", "Microsoft YaHei", sans-serif;
font-size: 16px; /* px / em / rem / % / vw */
font-weight: 400; /* 100~900 或 normal/bold */
font-style: italic; /* normal / italic / oblique */
line-height: 1.6; /* 无单位=倍数,推荐 */
letter-spacing: 0.5px; /* 字间距 */
word-spacing: 2px; /* 词间距 */
text-indent: 2em; /* 首行缩进 */
font-variant: small-caps; /* 小型大写 */
font-feature-settings: "liga"; /* 连字 */
}
简写 font:(顺序:style variant weight size/line-height family,size 和 family 必须写)
css
font: italic bold 16px/1.5 "Arial", sans-serif;
单位对比:
| 单位 | 相对 | 场景 |
|---|---|---|
px |
绝对 | 固定尺寸 |
em |
父元素字体 | 组件内相对尺寸 |
rem |
根元素字体 | 全局缩放 |
% |
父元素对应值 | 宽高、行高 |
vw / vh |
视口宽高 | 全屏布局 |
vmin / vmax |
视口较小/较大边 | 自适应字体 |
ch |
"0"字符宽 | 输入框宽度 |
ex |
"x"字符高 | 少用 |
5.2 文本
css
.text {
color: #333;
text-align: center; /* left/center/right/justify */
text-decoration: underline wavy red; /* 下划线,波浪线,红色 */
text-transform: uppercase; /* 大写 */
text-shadow: 2px 2px 4px rgba(0,0,0,.3);
white-space: nowrap; /* 不换行;normal 换行;pre 保留空白 */
word-break: break-all; /* 长英文强制换行 */
overflow-wrap: anywhere; /* 更智能的折行 */
hyphens: auto; /* 连字符 */
/* 单行溢出省略号 */
overflow: hidden;
text-overflow: ellipsis;
/* 多行溢出省略号 */
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
vertical-align: middle; /* 行内元素垂直对齐 */
direction: rtl; /* 文字方向 */
writing-mode: vertical-rl; /* 竖排 */
}
5.3 背景
css
.box {
/* 颜色 / 图片 / 渐变 */
background-color: #f5f5f5;
background-image: url('./bg.png');
background-image: linear-gradient(135deg, #667eea, #764ba2);
background-image: radial-gradient(circle, #fff, #333);
background-image: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
background-repeat: no-repeat; /* repeat-x / repeat-y / space / round */
background-position: center center; /* 可用百分比、关键字、长度 */
background-size: cover; /* cover / contain / 100px 200px */
background-attachment: fixed; /* 视差滚动效果 */
background-origin: content-box; /* 定位基准 */
background-clip: text; /* 文字裁剪背景(配透明色) */
background-blend-mode: multiply; /* 混合模式 */
}
简写:
css
background: #333 url('./bg.png') no-repeat center / cover;
/* 颜色 图片 重复 位置 / 尺寸 */
多背景(从上到下):
css
background:
url('./icon.png') no-repeat 10px center,
linear-gradient(to right, #fff, #eee);
文字渐变技巧:
css
.gradient-text {
background: linear-gradient(90deg, #3b82f6, #ec4899);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
5.4 边框与圆角
css
.box {
border: 2px solid #333; /* 简写 */
border-width: 1px 2px 3px 4px;
border-style: dashed;
border-color: red blue green gold;
border-top: 1px solid #eee; /* 单边 */
border-radius: 8px;
border-radius: 8px 0 8px 0; /* 四角 */
border-radius: 50%; /* 圆形 */
border-radius: 50% / 30%; /* 椭圆角 */
outline: 2px solid #3b82f6; /* 轮廓,不占空间 */
outline-offset: 2px;
box-shadow: 0 4px 12px rgba(0,0,0,.1), /* 多层阴影 */
0 1px 3px rgba(0,0,0,.06);
/* 参数:水平偏移 垂直偏移 模糊 扩展 颜色 inset */
}
CSS 边框也能做三角形:
css
.triangle {
width: 0; height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid #3b82f6;
}
5.5 溢出与光标
css
.box {
overflow: auto; /* visible / hidden / scroll / auto */
overflow-x: hidden;
overflow-y: auto;
overflow-clip-margin: 5px;
}
.btn { cursor: pointer; } /* default/pointer/text/move/not-allowed/... */
六、display 与元素类型
6.1 display 常用值
css
.el {
display: block; /* 块级:独占一行,可设宽高 */
display: inline; /* 行内:不换行,宽高无效 */
display: inline-block; /* 行内块:不换行 + 可设宽高 */
display: none; /* 隐藏,不占位 */
display: flex; /* 弹性容器 */
display: inline-flex;
display: grid;
display: inline-grid;
display: flow-root; /* BFC 容器 */
display: contents; /* 自身盒消失,子元素上提 */
}
6.2 三种元素对比
| 特性 | block | inline | inline-block |
|---|---|---|---|
| 换行 | ✅ | ❌ | ❌ |
| 设 width/height | ✅ | ❌ | ✅ |
| 垂直 margin | ✅ | ❌(不生效) | ✅ |
| 水平 margin | ✅ | ✅ | ✅ |
| padding | ✅ 全方向 | 仅左右有效(上下视觉生效不影响布局) | ✅ |
6.3 visibility vs display:none
css
.a { display: none; } /* 不渲染,不占空间,不可交互 */
.b { visibility: hidden; } /* 不可见,仍占空间 */
.c { opacity: 0; } /* 透明,仍占空间,可交互 */
6.4 float 浮动(了解)
css
.left { float: left; }
.right { float: right; }
/* 清除浮动 */
.clearfix::after {
content: "";
display: block;
clear: both;
}
现代布局优先 Flex/Grid ,
float只用于文字环绕图片等场景。
七、定位 position 与层叠上下文
7.1 position 五种取值
css
.static { position: static; } /* 默认,文档流 */
.relative { position: relative; } /* 相对自身原位置偏移,占位保留 */
.absolute { position: absolute; } /* 相对最近的已定位祖先偏移,脱离文档流 */
.fixed { position: fixed; } /* 相对视口,滚动不动 */
.sticky { position: sticky; } /* 滚动到阈值后粘住,top/bottom 等生效 */
配合的偏移属性:
css
.el {
top: 10px;
right: 20px;
bottom: 0;
left: 50%;
z-index: 10; /* 仅对定位元素(非 static)生效 */
}
7.2 定位的经典技巧
居中(绝对定位):
css
.parent { position: relative; }
.child {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
}
撑满父元素:
css
.overlay {
position: absolute;
inset: 0; /* 等价 top:0; right:0; bottom:0; left:0 */
}
吸顶导航:
css
.header {
position: sticky;
top: 0;
z-index: 100;
background: #fff;
}
固定底栏:
css
.footer {
position: fixed;
bottom: 0; left: 0; right: 0;
padding: 1rem;
}
7.3 层叠上下文(Stacking Context)
z-index 只在同一层叠上下文中比较。以下情况会创建新层叠上下文:
position非static且z-index非autoopacity< 1transform非nonefilter非nonewill-change指定以上属性isolation: isolatemix-blend-mode非normal
常见坑:
html
<div style="position: relative; z-index: 1">
<div style="position: absolute; z-index: 9999">A</div>
</div>
<div style="position: relative; z-index: 2">
B
</div>
<!-- A 永远盖不过 B,因为 A 被限制在父级 z-index:1 的上下文中 -->
同一上下文内的排序(从后到前):
- 背景与边框
- 负
z-index子元素 - 块级子元素
- 浮动子元素
- 行内子元素
z-index: 0/auto- 正
z-index子元素
八、Flexbox 弹性布局全解
8.1 基本概念
flex container(容器)
├─ main axis(主轴):默认水平
├─ cross axis(交叉轴):默认垂直
└─ flex items(项目)
[item] [item] [item]
↑ main-start ↑ main-end
html
<div class="flex-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
</div>
css
.flex-container { display: flex; }
8.2 容器属性(父元素上)
1. flex-direction:主轴方向
css
flex-direction: row; /* 默认:左→右 */
flex-direction: row-reverse; /* 右→左 */
flex-direction: column; /* 上→下 */
flex-direction: column-reverse; /* 下→上 */
2. flex-wrap:换行
css
flex-wrap: nowrap; /* 默认,不换行(可能挤压) */
flex-wrap: wrap; /* 换行 */
flex-wrap: wrap-reverse; /* 反向换行 */
3. justify-content:主轴对齐
css
justify-content: flex-start; /* 起点对齐 */
justify-content: flex-end; /* 终点对齐 */
justify-content: center; /* 居中 */
justify-content: space-between; /* 两端对齐,中间均分 */
justify-content: space-around; /* 每项两侧空间相等 */
justify-content: space-evenly; /* 所有间距完全相等 */
4. align-items:交叉轴对齐(单行)
css
align-items: stretch; /* 默认:拉满交叉轴 */
align-items: flex-start;
align-items: flex-end;
align-items: center;
align-items: baseline; /* 基线对齐 */
5. align-content:多行对齐(需 flex-wrap: wrap)
css
align-content: flex-start / flex-end / center /
space-between / space-around / space-evenly / stretch;
6. gap:项目间距(现代推荐)
css
gap: 16px; /* 行、列均 16 */
gap: 10px 20px; /* 行 10,列 20 */
row-gap: 10px;
column-gap: 20px;
8.3 项目属性(子元素上)
1. flex(最重要)
css
/* 简写:flex-grow flex-shrink flex-basis */
flex: 1; /* = 1 1 0%:平均分配剩余空间 */
flex: auto; /* = 1 1 auto */
flex: none; /* = 0 0 auto:不伸不缩 */
flex: 0 0 200px; /* 固定 200px 不可伸缩 */
flex-grow:空间富余时放大比例,默认 0flex-shrink:空间不足时缩小比例,默认 1flex-basis:主轴上的基础尺寸,默认 auto
2. align-self:单独覆盖 align-items
css
align-self: flex-start / center / flex-end / stretch;
3. order:排列顺序,数字越小越靠前
css
order: -1;
8.4 Flex 经典布局案例
① 水平垂直居中:
css
.parent {
display: flex;
justify-content: center;
align-items: center;
}
② 导航栏(左 logo,右菜单):
css
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
}
③ 两栏布局(左固定,右自适应):
css
.layout { display: flex; gap: 20px; }
.sidebar { flex: 0 0 220px; }
.main { flex: 1; min-width: 0; } /* min-width: 0 防溢出 */
④ 圣杯布局(头/中/尾,中间三栏):
css
body { display: flex; flex-direction: column; min-height: 100vh; }
main { display: flex; flex: 1; }
.left, .right { flex: 0 0 200px; }
.center { flex: 1; }
⑤ 等分布局:
css
.row { display: flex; }
.row > * { flex: 1; }
⑥ 卡片列表自动换行:
css
.cards {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.cards > .card {
flex: 1 1 260px; /* 基础宽 260,可伸可缩 */
}
九、Grid 网格布局全解
9.1 基本概念
grid container(容器)
├─ grid lines(网格线,从 1 开始计数)
├─ grid tracks(行/列轨道)
├─ grid cell(单元格)
└─ grid area(网格区域,可跨行列)
css
.grid {
display: grid;
grid-template-columns: 200px 1fr 100px;
grid-template-rows: 80px auto;
gap: 16px;
}
9.2 容器属性
1. 定义列/行:
css
/* 固定尺寸 */
grid-template-columns: 100px 200px 100px;
/* 混合单位 */
grid-template-columns: 200px 1fr 2fr;
/* repeat 简化 */
grid-template-columns: repeat(4, 1fr); /* 4 等份 */
grid-template-columns: repeat(3, 100px);
/* 自动填充 / 自动适应 */
grid-template-columns: repeat(auto-fill, 200px); /* 尽量塞满,可能留空 */
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* 填满 */
/* minmax + 响应式 */
grid-template-columns: minmax(200px, 1fr) 2fr;
/* 命名网格线 */
grid-template-columns: [start] 100px [mid] 1fr [end];
/* 命名区域 */
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
2. 行高:
css
grid-template-rows: 80px auto 60px;
grid-auto-rows: 100px; /* 隐式行高度 */
grid-auto-columns: 100px;
3. 间距:
css
gap: 16px;
row-gap: 20px;
column-gap: 10px;
4. 对齐:
css
/* 所有单元格内部内容的对齐 */
justify-items: start | end | center | stretch;
align-items: start | end | center | stretch;
/* 整个网格在容器内的对齐 */
justify-content: start | end | center | space-between |
space-around | space-evenly;
align-content: 同上;
5. 自动排列方向:
css
grid-auto-flow: row; /* 默认:按行填 */
grid-auto-flow: column; /* 按列填 */
grid-auto-flow: row dense; /* 密集排列,填补空洞 */
9.3 项目属性
1. 按网格线定位:
css
.item {
grid-column-start: 1;
grid-column-end: 3; /* 跨 2 列(1 到 3) */
grid-row-start: 2;
grid-row-end: 4;
/* 简写 */
grid-column: 1 / 3;
grid-row: 2 / 4;
/* 跨 N 格 */
grid-column: span 2;
/* 用负线号(从末尾数) */
grid-column: 1 / -1; /* 撑满整行 */
}
2. 用命名区域定位:
css
.layout {
display: grid;
grid-template-areas:
"header header"
"sidebar main"
"footer footer";
grid-template-columns: 200px 1fr;
grid-template-rows: 60px 1fr 60px;
min-height: 100vh;
gap: 12px;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }
3. 自身对齐:
css
.item {
justify-self: start | end | center | stretch;
align-self: start | end | center | stretch;
}
9.4 Grid 经典布局案例
① 响应式卡片墙:
css
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 20px;
}
② 12 列栅格系统:
css
.row {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 16px;
}
.col-6 { grid-column: span 6; }
.col-4 { grid-column: span 4; }
③ 页面对角布局(大屏):
css
.page {
display: grid;
grid-template-areas:
"nav nav"
"hero sidebar"
"content content"
"footer footer";
grid-template-columns: 1fr 300px;
grid-template-rows: 60px 400px auto 80px;
}
9.5 Flex vs Grid 选择
| 场景 | 推荐 |
|---|---|
| 一维布局(一行或一列) | Flex |
| 二维布局(行+列同时) | Grid |
| 内容驱动尺寸 | Flex |
| 布局驱动尺寸 | Grid |
| 导航、按钮组、卡片内布局 | Flex |
| 页面骨架、杂志排版、卡片墙 | Grid |
实践中两者混用,Grid 做外层骨架,Flex 做组件内部。
十、过渡、变形、动画
10.1 过渡 transition
css
.btn {
background: #3b82f6;
transition: background-color .3s ease, transform .2s ease-out;
/* transition: all .3s; 简单但性能差,不推荐 */
}
.btn:hover {
background: #2563eb;
transform: translateY(-2px);
}
完整语法:
css
transition: [property] [duration] [timing-function] [delay];
transition: background-color 0.3s cubic-bezier(.4,0,.2,1) 0.1s;
缓动函数:
| 值 | 效果 |
|---|---|
linear |
匀速 |
ease |
慢-快-慢(默认) |
ease-in |
慢进 |
ease-out |
慢出 |
ease-in-out |
两端慢 |
cubic-bezier(x1,y1,x2,y2) |
自定义贝塞尔 |
性能提示 :优先过渡 transform 和 opacity(触发 GPU 合成),避免过渡 width/height/top/left(触发重排)。
10.2 变形 transform
css
.el {
/* 平移 */
transform: translate(10px, 20px);
transform: translateX(10px);
transform: translate3d(10px, 20px, 0);
/* 缩放 */
transform: scale(1.2);
transform: scaleX(2) scaleY(0.5);
/* 旋转 */
transform: rotate(45deg);
transform: rotateX(45deg); /* 3D */
transform: rotateY(45deg);
/* 倾斜 */
transform: skew(10deg, 5deg);
/* 多重变形:从右往左应用 */
transform: translate(-50%, -50%) rotate(45deg) scale(1.2);
/* 3D */
transform: perspective(500px) rotateY(30deg);
transform-style: preserve-3d;
backface-visibility: hidden;
transform-origin: center center; /* 变换原点 */
}
经典应用:
- 居中:
translate(-50%, -50%) - 悬停浮起:
transform: translateY(-4px) - 加载动画旋转:
animation: spin 1s linear infinite; @keyframes spin { to { transform: rotate(360deg); } } - 翻转卡片:
rotateY(180deg)+backface-visibility
10.3 关键帧动画 @keyframes
css
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.card {
animation-name: fadeInUp;
animation-duration: 0.5s;
animation-timing-function: ease-out;
animation-delay: 0.1s;
animation-iteration-count: 1; /* infinite 无限 */
animation-direction: normal; /* reverse / alternate */
animation-fill-mode: both; /* forwards / backwards */
animation-play-state: running; /* paused */
/* 简写 */
animation: fadeInUp 0.5s ease-out 0.1s 1 normal both;
}
简写顺序(时间值):第一个是 duration,第二个是 delay。
animation-fill-mode 详解:
| 值 | 含义 |
|---|---|
none |
动画前后无样式 |
forwards |
保留最后一帧样式 |
backwards |
应用第一帧到延时期间 |
both |
前后都保留 |
10.4 常用动画案例
加载圈:
css
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
width: 40px; height: 40px;
border: 4px solid #e2e8f0;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin .8s linear infinite;
}
骨架屏闪烁:
css
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.skeleton {
background: linear-gradient(90deg, #eee 25%, #f5f5f5 37%, #eee 63%);
background-size: 200% 100%;
animation: shimmer 1.4s infinite;
}
脉冲:
css
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.pulse { animation: pulse 2s ease-in-out infinite; }
10.5 prefers-reduced-motion(无障碍)
css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
十一、CSS 变量与函数
11.1 自定义属性(CSS Variables)
css
:root {
--color-primary: #3b82f6;
--space-4: 16px;
--radius: 8px;
}
.btn {
background: var(--color-primary);
padding: var(--space-4);
border-radius: var(--radius);
}
/* 带默认值 */
color: var(--text-color, #333);
/* 局部覆盖 */
.dark { --color-primary: #60a5fa; }
/* JS 读写 */
/* el.style.setProperty('--color-primary', 'red'); */
/* getComputedStyle(el).getPropertyValue('--color-primary'); */
优势:
- 主题切换、响应式、JS 动态修改
- 全局统一管理设计变量
- 可继承、可作用域
11.2 常用函数
颜色:
css
color: rgb(59 130 246);
color: rgba(59, 130, 246, 0.5);
color: hsl(217 91% 60%);
color: #3b82f6;
color: #3b82f680; /* 带透明度 */
color: color-mix(in srgb, #3b82f6 50%, white);
尺寸计算:
css
width: calc(100% - 32px);
height: calc(100vh - 60px);
font-size: calc(1rem + 0.5vw);
最值:
css
width: min(90%, 1200px); /* 取较小 */
width: max(320px, 50%); /* 取较大 */
font-size: clamp(1rem, 2.5vw, 1.5rem); /* min, 理想值, max */
其他:
css
/* 变量 */
var(--x)
/* 引用属性值 */
width: attr(data-width px);
/* 环境变量(iOS 安全区) */
padding-bottom: env(safe-area-inset-bottom);
11.3 clamp() 响应式排版
css
h1 { font-size: clamp(1.75rem, 5vw, 3rem); }
.container { width: clamp(320px, 90vw, 1280px); margin-inline: auto; }
十二、媒体查询与响应式设计
12.1 基本语法
css
@media screen and (min-width: 768px) {
.container { max-width: 720px; }
}
@media (max-width: 767px) {
.sidebar { display: none; }
}
/* 组合条件 */
@media (min-width: 600px) and (max-width: 900px) { }
@media (min-width: 600px) and (orientation: landscape) { }
/* or */
@media (max-width: 600px), (min-width: 1200px) { }
/* 深色模式 */
@media (prefers-color-scheme: dark) { }
/* 打印 */
@media print { .no-print { display: none; } }
/* 悬停能力 */
@media (hover: hover) { .btn:hover { ... } }
12.2 断点策略
移动优先(Mobile First)------推荐:
css
/* 基础样式:手机 */
.container { width: 100%; padding: 16px; }
/* 平板 ≥ 640 */
@media (min-width: 640px) {
.container { max-width: 600px; padding: 24px; }
}
/* 桌面 ≥ 1024 */
@media (min-width: 1024px) {
.container { max-width: 960px; }
}
/* 大屏 ≥ 1280 */
@media (min-width: 1280px) {
.container { max-width: 1200px; }
}
12.3 常用响应式技巧
① 响应式图片:
css
img { max-width: 100%; height: auto; display: block; }
② 响应式网格(无需媒体查询):
css
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 16px;
}
③ 响应式字体:
css
html { font-size: clamp(14px, 1vw + 10px, 18px); }
④ 容器查询(现代):
css
.card-container { container-type: inline-size; }
@container (min-width: 400px) {
.card { display: flex; }
}
⑤ 逻辑属性(支持 RTL):
css
.el {
margin-inline: auto; /* 替代左右 */
margin-block: 16px; /* 替代上下 */
padding-inline: 12px;
border-inline-start: 2px solid;
}
12.4 视口单位与安全区
css
.hero {
min-height: 100vh;
min-height: 100dvh; /* 动态视口,移动端更准 */
min-height: 100svh; /* 小视口,最保守 */
}
十三、现代 CSS 新特性
13.1 嵌套(CSS Nesting)
css
.card {
padding: 16px;
& .title { font-size: 1.25rem; }
&:hover { box-shadow: 0 4px 12px rgba(0,0,0,.1); }
&.is-active { border-color: #3b82f6; }
@media (min-width: 768px) {
padding: 24px;
}
}
13.2 逻辑属性
css
.el {
margin-inline: auto;
padding-block: 1rem;
border-inline-start: 3px solid var(--color-primary);
inset-inline-end: 0;
}
13.3 aspect-ratio
css
.thumb {
aspect-ratio: 16 / 9;
object-fit: cover;
}
13.4 object-fit / object-position
css
img {
width: 200px; height: 200px;
object-fit: cover; /* cover / contain / fill / none / scale-down */
object-position: center top;
}
13.5 scroll-behavior 与平滑滚动
css
html { scroll-behavior: smooth; }
13.6 scroll-snap 滚动吸附
css
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.carousel > .slide {
flex: 0 0 100%;
scroll-snap-align: start;
}
13.7 :focus-visible
css
/* 鼠标点击不出轮廓,键盘 Tab 出轮廓 */
button:focus { outline: none; }
button:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; }
13.8 accent-color
css
input[type="checkbox"],
input[type="radio"],
progress {
accent-color: #3b82f6;
}
13.9 backdrop-filter
css
.glass {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(12px) saturate(150%);
-webkit-backdrop-filter: blur(12px) saturate(150%);
}
13.10 text-wrap
css
h1 { text-wrap: balance; } /* 标题行更均衡 */
p { text-wrap: pretty; } /* 避免孤字 */
13.11 color-scheme
css
:root { color-scheme: light dark; }
十四、项目实战:TaskFlow 完整样式
现在把第一篇的 HTML 骨架变成漂亮的成品。
14.1 完整 css/main.css
css
/* ============================================================
TaskFlow · 主样式
依赖:reset.css、variables.css
============================================================ */
/* ---------- 基础 ---------- */
body {
background: var(--bg-page);
color: var(--text-primary);
min-height: 100vh;
display: flex;
flex-direction: column;
transition: background-color .3s, color .3s;
}
::selection { background: var(--color-primary); color: #fff; }
/* 焦点可见性 */
:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-radius: var(--radius-sm);
}
/* ---------- 页头 ---------- */
.app-header {
position: sticky;
top: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-4);
padding: var(--space-3) var(--space-6);
background: color-mix(in srgb, var(--bg-card) 85%, transparent);
backdrop-filter: blur(12px) saturate(180%);
-webkit-backdrop-filter: blur(12px) saturate(180%);
border-bottom: 1px solid var(--border-color);
}
.brand {
display: flex;
align-items: center;
gap: var(--space-2);
}
.brand h1 {
font-size: 1.25rem;
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(135deg, var(--color-primary), #ec4899);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.nav-list {
display: flex;
gap: var(--space-1);
}
.nav-link {
display: inline-block;
padding: var(--space-2) var(--space-3);
border-radius: var(--radius-md);
font-size: 0.9rem;
color: var(--text-secondary);
transition: background-color .2s, color .2s;
}
.nav-link:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.nav-link.is-active {
background: var(--color-primary);
color: #fff;
}
/* ---------- 主体 ---------- */
.app-main {
flex: 1;
width: 100%;
max-width: 860px;
margin-inline: auto;
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
/* 卡片容器 */
.panel {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: var(--space-5);
box-shadow: var(--shadow-sm);
transition: box-shadow .3s;
}
.panel:hover { box-shadow: var(--shadow-md); }
.panel-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--space-3);
}
.panel-head h2 { font-size: 1.1rem; }
/* ---------- 表单 ---------- */
.task-form {
display: grid;
grid-template-columns: 1fr auto auto auto;
gap: var(--space-2);
align-items: center;
}
@media (max-width: 720px) {
.task-form {
grid-template-columns: 1fr;
}
}
.field { position: relative; }
.task-form input[type="text"],
.task-form input[type="date"],
.task-form select,
.dialog-form input,
.dialog-form select {
width: 100%;
padding: var(--space-2) var(--space-3);
background: var(--bg-page);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
font-size: 0.95rem;
color: var(--text-primary);
transition: border-color .2s, box-shadow .2s;
}
.task-form input:focus,
.task-form select:focus,
.dialog-form input:focus,
.dialog-form select:focus {
border-color: var(--color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent);
outline: none;
}
.task-form input:invalid:not(:placeholder-shown) {
border-color: var(--color-danger);
}
.form-error {
margin-top: var(--space-2);
color: var(--color-danger);
font-size: 0.85rem;
}
/* ---------- 按钮 ---------- */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-1);
padding: var(--space-2) var(--space-4);
border-radius: var(--radius-md);
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: transform .15s, background-color .2s, box-shadow .2s;
white-space: nowrap;
}
.btn:active { transform: translateY(1px) scale(0.98); }
.btn-primary {
background: var(--color-primary);
color: #fff;
box-shadow: 0 2px 8px color-mix(in srgb, var(--color-primary) 30%, transparent);
}
.btn-primary:hover {
background: var(--color-primary-dark);
box-shadow: 0 4px 12px color-mix(in srgb, var(--color-primary) 40%, transparent);
}
.btn-ghost {
background: transparent;
color: var(--text-secondary);
border: 1px solid var(--border-color);
}
.btn-ghost:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.icon-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: var(--radius-md);
font-size: 1rem;
cursor: pointer;
color: var(--text-secondary);
transition: background-color .2s, transform .2s;
}
.icon-btn:hover {
background: var(--bg-hover);
transform: scale(1.08);
}
/* ---------- 统计 ---------- */
.stats { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.stat-list {
display: flex;
gap: var(--space-4);
flex: 1;
}
.stat {
display: flex;
flex-direction: column;
min-width: 60px;
}
.stat dt { font-size: 0.75rem; color: var(--text-muted); }
.stat dd {
font-size: 1.5rem;
font-weight: 700;
font-variant-numeric: tabular-nums;
color: var(--color-primary);
}
.progress-wrap { flex: 1 1 200px; }
progress {
width: 100%;
height: 8px;
border: none;
border-radius: 999px;
overflow: hidden;
accent-color: var(--color-primary);
}
progress::-webkit-progress-bar {
background: var(--bg-hover);
border-radius: 999px;
}
progress::-webkit-progress-value {
background: linear-gradient(90deg, var(--color-primary), #ec4899);
border-radius: 999px;
transition: width .4s ease;
}
progress::-moz-progress-bar {
background: linear-gradient(90deg, var(--color-primary), #ec4899);
border-radius: 999px;
}
/* ---------- 任务列表 ---------- */
.task-list {
display: flex;
flex-direction: column;
gap: var(--space-2);
list-style: none;
}
.task-item {
display: grid;
grid-template-columns: 1fr auto auto;
align-items: center;
gap: var(--space-3);
padding: var(--space-3) var(--space-4);
background: var(--bg-page);
border: 1px solid var(--border-color);
border-left: 4px solid var(--color-primary);
border-radius: var(--radius-md);
transition: transform .2s, box-shadow .2s, opacity .3s;
animation: taskIn .25s ease-out both;
}
@keyframes taskIn {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
.task-item:hover {
transform: translateX(2px);
box-shadow: var(--shadow-sm);
}
.task-item[data-priority="high"] { border-left-color: var(--color-danger); }
.task-item[data-priority="normal"] { border-left-color: var(--color-primary); }
.task-item[data-priority="low"] { border-left-color: var(--color-success); }
.task-item[data-done="true"] {
opacity: 0.55;
border-left-color: var(--text-muted);
}
.task-item[data-done="true"] .task-title {
text-decoration: line-through;
color: var(--text-muted);
}
/* 自定义复选框 */
.task-check {
display: flex;
align-items: center;
gap: var(--space-3);
cursor: pointer;
min-width: 0;
}
.task-check input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 1px; height: 1px;
}
.checkmark {
flex: 0 0 auto;
width: 20px;
height: 20px;
border: 2px solid var(--border-color);
border-radius: 6px;
display: grid;
place-items: center;
transition: background-color .2s, border-color .2s, transform .15s;
}
.checkmark::after {
content: "";
width: 5px; height: 9px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg) scale(0);
transition: transform .15s;
margin-top: -2px;
}
.task-check input:checked + .checkmark {
background: var(--color-success);
border-color: var(--color-success);
}
.task-check input:checked + .checkmark::after {
transform: rotate(45deg) scale(1);
}
.task-check input:focus-visible + .checkmark {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
.task-title {
font-size: 0.95rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 元信息 */
.task-meta {
display: flex;
align-items: center;
gap: var(--space-2);
font-size: 0.75rem;
color: var(--text-muted);
}
.badge {
padding: 2px 8px;
border-radius: 999px;
font-weight: 600;
font-size: 0.7rem;
}
.badge-high { background: color-mix(in srgb, var(--color-danger) 15%, transparent); color: var(--color-danger); }
.badge-normal { background: color-mix(in srgb, var(--color-primary) 15%, transparent); color: var(--color-primary); }
.badge-low { background: color-mix(in srgb, var(--color-success) 15%, transparent); color: var(--color-success); }
.task-item[data-done="true"] time { text-decoration: line-through; }
.task-delete { opacity: 0; transition: opacity .2s, transform .2s; }
.task-item:hover .task-delete,
.task-delete:focus-visible { opacity: 1; }
.task-delete:hover { color: var(--color-danger); }
/* ---------- 空状态 ---------- */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
padding: var(--space-8) var(--space-4);
color: var(--text-muted);
text-align: center;
}
.empty-state img { opacity: 0.6; }
/* ---------- 页脚 ---------- */
.app-footer {
padding: var(--space-4) var(--space-6);
text-align: center;
font-size: 0.8rem;
color: var(--text-muted);
border-top: 1px solid var(--border-color);
}
.app-footer a { color: var(--color-primary); }
.app-footer a:hover { text-decoration: underline; }
.app-footer abbr { cursor: help; }
/* ---------- 弹窗 ---------- */
.dialog {
border: none;
border-radius: var(--radius-lg);
padding: 0;
background: var(--bg-card);
color: var(--text-primary);
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
max-width: 420px;
width: calc(100% - 32px);
}
.dialog::backdrop {
background: rgba(15, 23, 42, 0.5);
backdrop-filter: blur(4px);
animation: fadeIn .2s ease;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.dialog[open] { animation: dialogIn .25s cubic-bezier(.4,0,.2,1); }
@keyframes dialogIn {
from { opacity: 0; transform: translateY(16px) scale(0.96); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
.dialog-form {
display: flex;
flex-direction: column;
gap: var(--space-3);
padding: var(--space-5);
}
.dialog-form h2 { font-size: 1.1rem; margin-bottom: var(--space-1); }
.dialog-form label {
font-size: 0.85rem;
color: var(--text-secondary);
margin-bottom: calc(var(--space-1) * -1);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: var(--space-2);
margin-top: var(--space-2);
}
/* ---------- 深色主题切换过渡 ---------- */
* {
transition-property: background-color, border-color, color;
transition-duration: .25s;
transition-timing-function: ease;
}
14.2 主题切换 JS(后续篇章详解)
javascript
// js/main.js 片段
const themeToggle = document.getElementById('themeToggle');
const savedTheme = localStorage.getItem('taskflow.theme');
if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
}
themeToggle.addEventListener('click', () => {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
if (isDark) {
document.documentElement.removeAttribute('data-theme');
localStorage.setItem('taskflow.theme', 'light');
} else {
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('taskflow.theme', 'dark');
}
});
14.3 用到的 CSS 知识点索引
| 知识点 | 项目位置 |
|---|---|
| CSS 变量 | variables.css 全局 |
| 层叠与继承 | 全局 body、* 过渡 |
盒模型 / box-sizing |
reset.css |
BFC / flow-root |
页头 backdrop 隐含 |
| Flex | 页头、按钮、统计区、dialog-actions |
| Grid | .task-form、.task-item、.panel |
grid-template-columns: 1fr auto auto |
任务项 |
定位 sticky |
.app-header |
定位 absolute |
.task-check input 隐藏、::backdrop |
z-index |
页头 |
伪类 :hover :focus-visible :checked :not |
多处 |
伪元素 ::after ::backdrop ::-webkit-progress-value |
复选框、弹窗、进度条 |
属性选择器 [data-priority] [data-done] |
任务项状态 |
transition |
全局过渡 |
transform + translate + scale + rotate |
复选勾、按钮、弹窗 |
@keyframes |
taskIn、fadeIn、dialogIn |
linear-gradient |
logo 文字、进度条 |
color-mix() |
主题色派生 |
clamp() / minmax() |
响应式布局 |
aspect-ratio(可扩展) |
图片 |
| 媒体查询 | .task-form 移动端单列 |
prefers-reduced-motion |
可加在末尾 |
backdrop-filter |
页头、dialog backdrop |
object-fit |
图片(可扩展) |
text-overflow: ellipsis |
任务标题 |
font-variant-numeric |
统计数字 |
逻辑属性 margin-inline |
容器居中 |
14.4 关键视觉细节讲解
① 半透明毛玻璃页头
css
background: color-mix(in srgb, var(--bg-card) 85%, transparent);
backdrop-filter: blur(12px) saturate(180%);
用 color-mix 让卡片色变成 85% 透明,配 backdrop-filter 实现 iOS 风格毛玻璃。
② 自定义复选框
原生的 checkbox 各浏览器样式不一,我们用:
css
/* 隐藏原生 */
input[type="checkbox"] { position: absolute; opacity: 0; }
/* 用 span 自绘方框 */
.checkmark { border: 2px solid; border-radius: 6px; }
/* 用 ::after + 旋转做勾 */
.checkmark::after {
content: ""; width: 5px; height: 9px;
border: solid #fff; border-width: 0 2px 2px 0;
transform: rotate(45deg) scale(0);
}
/* 勾选时放大 */
input:checked + .checkmark::after { transform: rotate(45deg) scale(1); }
这是纯 CSS 打勾动画的经典写法。
③ 任务状态用 data-* 驱动样式
css
.task-item[data-priority="high"] { border-left-color: var(--color-danger); }
.task-item[data-done="true"] .task-title { text-decoration: line-through; }
JS 只需改 dataset.priority,样式自动跟随,样式与逻辑解耦。
④ 深色主题切换
variables.css 里的 [data-theme="dark"] 覆盖变量,html 上切换 data-theme 属性,所有引用变量的元素瞬间换色,无需重写任何组件样式。
⑤ 入场动画
css
.task-item { animation: taskIn .25s ease-out both; }
新增任务时自动播放,both 让动画结束保留终态。
14.5 运行效果检查
打开 index.html 应看到:
- 顶部毛玻璃导航栏,logo 渐变文字
- 中间添加任务表单(桌面 4 列,手机单列)
- 统计卡片 + 渐变进度条
- 任务列表,左侧彩条表示优先级,hover 浮起
- 勾选任务后变灰 + 删除线
- 点击 🌙 切深色模式
- 点击任务区(若 JS 已实现)弹出 dialog
14.6 Lighthouse 优化建议
跑一次 Chrome DevTools → Lighthouse,注意:
- 颜色对比度 :
--text-muted若太浅可能不达标,调整到#64748b以上 - 可点击区域:图标按钮至少 44×44px(本项目 36px,可加大)
- 字体可读性:正文不小于 16px(本项目 0.95rem ≈ 15.2px,可提升到 1rem)
- 动画 :追加
prefers-reduced-motion规则
十五、本篇小结与练习
知识点回顾
| 模块 | 核心要点 |
|---|---|
| 引入方式 | 行内 / style / link,link 最优 |
| 选择器 | 基础、组合、属性、伪类、伪元素、:is/:where/:has |
| 优先级 | 行内 > ID > 类/属性/伪类 > 元素/伪元素 |
| 层叠继承 | 来源→优先级→顺序;inherit/initial/unset |
| 盒模型 | content-box / border-box;margin 塌陷;BFC |
| 常用属性 | 字体、文本、背景、边框、阴影、圆角 |
| display | block / inline / inline-block / flex / grid / none |
| 定位 | static / relative / absolute / fixed / sticky;层叠上下文 |
| Flex | 容器 6 属性 + 项目 3 属性 + 经典布局 |
| Grid | 行列定义 + 区域命名 + auto-fit/minmax |
| 动画 | transition、transform、@keyframes |
| 变量函数 | var()、calc()、clamp()、min/max、color-mix() |
| 响应式 | 移动优先、媒体查询、clamp、容器查询、逻辑属性 |
| 新特性 | 嵌套、aspect-ratio、object-fit、scroll-snap、backdrop-filter |
动手练习
- 基础 :用纯 CSS 做一个"汉堡菜单"图标(三横线变 X),要求用
::before/::after和transform过渡。 - 进阶 :用 Grid 实现一个"图片瀑布流"(
grid-auto-rows+span),再用 Flex 实现同样效果对比。 - 挑战 :给 TaskFlow 加上"卡片视图/列表视图"切换,用
body[data-view="card"]切换 Grid/Flex 布局,并加过渡动画。
常见坑清单
- ❌ 忘记
box-sizing: border-box→ 尺寸计算爆炸 - ❌ 用
transition: all→ 性能差、意外过渡 - ❌ 父子 margin 塌陷没处理 → 布局错位
- ❌
z-index不生效 → 父级建了新层叠上下文 - ❌
flex: 1内部内容溢出 → 加min-width: 0 - ❌ 移动端 100vh 高度抖动 → 用
100dvh - ❌ 只写
:hover不写:focus-visible→ 键盘用户体验差 - ❌ 用
!important抢救样式 → 恶性循环 - ❌ 忽略
prefers-reduced-motion→ 无障碍扣分 - ❌ 颜色硬编码不抽变量 → 主题无法切换
下一篇预告
第三篇:JavaScript 基础语法全解
- JS 的引入、执行环境、
<script>与模块 - 变量
var/let/const与作用域、TDZ、提升 - 数据类型全解(7 种原始 + 引用)+
typeof - 类型转换(隐式/显式)
- 运算符全解(算术、比较、逻辑、位、空值合并、可选链)
- 流程控制(if/switch/for/while/for-in/for-of)
- 函数(声明、表达式、箭头、参数、默认值、剩余、回调、纯函数)
- 数组方法全解(
map/filter/reduce/sort/...) - 对象、
this、call/apply/bind - 错误处理、严格模式
- 实战:用 JS 实现 TaskFlow 的 localStorage 与基础逻辑(第四篇进一步接 DOM 事件)
继续把第二篇的 CSS 保存好,第三篇我们让页面"动起来"。🚀