开始根据林想的Web工坊大佬的建议路线学习CSS。
本文涉及内容会如下:
Introduction to the CSS box model - CSS | MDN
Understanding and setting aspect ratios - CSS | MDN
Introduction to the CSS box model(CSS 盒模型入门)
浏览器渲染引擎在排版文档时,把每一个元素都表示为一个矩形盒子(rectangular box)。CSS 决定这些盒子的尺寸(size)、位置(position)和外观属性(颜色、背景、边框粗细等)。
每个盒子由内到外包含四个区域,各自有一条边界(edge):
1
2
3
4
5
6
7
8
9
10
11
12
13
┌──────────────────────────────────┐
│ margin area │ ← margin edge(外边距边界)
│ ┌──────────────────────────┐ │
│ │ border area │ │ ← border edge(边框边界)
│ │ ┌──────────────────┐ │ │
│ │ │ padding area │ │ │ ← padding edge(内边距边界)
│ │ │ ┌──────────┐ │ │ │
│ │ │ │ content │ │ │ │ ← content edge(内容边界)
│ │ │ │ area │ │ │ │
│ │ │ └──────────┘ │ │ │
│ │ └──────────────────┘ │ │
│ └──────────────────────────┘ │
└──────────────────────────────────┘
整个页面就是一堆盒子摞起来的。
Content area(内容区)
内容区紧贴 content edge 内侧,放元素的”真正的”内容,例如文字、图片、视频播放器等。其尺寸称为 content-box width / content-box height,通常有背景色或背景图。
当 box-sizing 为默认值 content-box 且元素为 block 元素时,可以用这些属性显式定义内容区大小:
width、min-width、max-width、height、min-height、max-height
Padding area(内边距区)
padding 区在 content area 外面,由 padding edge 包围。尺寸称为 padding-box width / padding-box height。padding 的粗细由以下属性控制:
padding-top、padding-right、padding-bottom、padding-left,以及简写属性 padding
通俗理解:padding 是内容到边框之间的”填充物”,让内容和边框别贴太紧。
Border area(边框区)
border 区在 padding area 外面,由 border edge 包围。尺寸称为 border-box width / border-box height。边框粗细由以下属性控制:
border-width 以及简写属性 border
关键点一:box-sizing: border-box
当设为 border-box 时,width/height 定义的是 border area 的尺寸(即包含了 padding 和 border),而不是 content area 的尺寸。这就是为什么前端工程里几乎所有项目都设 box-sizing: border-box——元素总宽等于你设的 width,不用手动加减 padding 和 border。
| 值 | width 定义的是 | 最终元素总宽 |
|---|---|---|
content-box(默认) | content area 宽度 | width + padding + border |
border-box | border area 宽度(含 padding + border) | width(不用再加) |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* content-box */
.box-content {
box-sizing: content-box;
width: 300px;
padding: 20px;
border: 5px solid;
/* 实际占用:300 + 20*2 + 5*2 = 350px */
}
/* border-box */
.box-border {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 5px solid;
/* 实际占用:正好 300px */
}
几乎所有 reset CSS / normalize.css 的第一件事就是把所有元素设为
border-box。
关键点二:背景延伸到哪
默认情况下,背景(background-color / background-image)会延伸到 border 的外边缘(在 z 轴上延伸到下方)。可以用 background-clip 属性改变这个行为。
Margin area(外边距区)
margin 区在 border area 外面,由 margin edge 包围。尺寸称为 margin box width / margin box height。这是一个空白区域,用来把元素和邻居隔开。大小由以下属性控制:
margin-top、margin-right、margin-bottom、margin-left,以及简写属性 margin
⚠️ margin collapsing(外边距折叠)
当上下相邻的两个块级元素的 margin 相遇时,它们会”折叠”,不是相加,而是取最大值。此时 margin area 没有明确边界,因为 margin 被两个盒子共享了。
CSS 规范只定义了 block direction(垂直方向)的 margin collapsing,水平方向并未设计这个规则。
原因在于 CSS1 时代的设计假设:
1 2 3 4 5 6 7垂直:block 元素默认堆叠排列 段落间距天然靠 margin-bottom + margin-top 如果两个 20px 叠出 40px,段落间距太宽 → 需要折叠 水平:inline 元素默认排成一行 间距用 margin-left/right 控制,每个元素的间距是独立的 没有"两个 box 相邻 margin 打架"的问题 → 不需要折叠而 Flexbox 更进一步,连垂直方向的折叠也不发生了。
margin collapsing 只在正常流(normal flow)的 block 元素之间生效。Flex 容器里的子项(flex items)虽然看起来也是”盒子排在一起”,但它们的布局模型已经不是 normal flow 了,margin 完全独立,上下左右各管各的。
行内非替换元素的特殊行为
对于 non-replaced inline elements(如 <span>、<a>):
- 边框和内边距仍然显示在内容周围
- 但元素在行高上的占位由
line-height决定,而不是由 border/padding 撑开
一个
<span>设了padding: 20px,上下 padding 视觉上有,但不撑开行高,可能会盖住上下行的文字。
四个区域速查
| 区域 | 边界 | 尺寸名称 | 核心 CSS |
|---|---|---|---|
| Content | content edge | content-box width/height | width, height |
| Padding | padding edge | padding-box width/height | padding |
| Border | border edge | border-box width/height | border, border-width |
| Margin | margin edge | margin box width/height | margin |
盒模型 = content → padding → border → margin,从内到外四层。默认
content-box下width只管内容区,border-box下width管到 border 区。前端开发中统一用
border-box是常识,减少了很多心智负担。
Mastering margin collapsing(margin 折叠详解)
块级元素的 margin-top 和 margin-bottom 有时会合并(折叠,collapse)为单个 margin,其大小等于各 margin 中最大者(相等时取其中一个),这就是 margin collapsing。
浮动元素和绝对定位元素的 margin 永远不会折叠。
margin 折叠只发生在垂直方向。
margin 折叠发生在三种基本情形中:
Adjacent siblings(相邻兄弟元素)
相邻兄弟元素的 margin 会折叠,除非后一个兄弟需要 clear 清除浮动。
1
2
<p style="margin-bottom: 30px;">上面的段落,margin-bottom 30px</p>
<p style="margin-top: 20px;">下面的段落,margin-top 20px</p>
两个段落之间的间距是 max(30px, 20px) = 30px,而不是 30 + 20 = 50px。
No content separating parent and descendants(父子间无隔离物)
父块级元素与其后代元素之间的垂直 margin 也会折叠,前提是它们之间没有隔离物。具体分为两种情况:
情况一:父元素的 margin-top 与第一个 in-flow 后代的 margin-top 折叠
除非父元素有:
border-toppadding-top- 任何 inline 内容(如文字)
- clearance(
clear产生的)
情况二:父元素的 margin-bottom 与最后一个 in-flow 后代的 margin-bottom 折叠
除非父元素有:
- 显式定义的
height或min-height border-bottompadding-bottom
两种情况下,给父元素创建新的 block formatting context(BFC) 也能阻止 margin 折叠。
举例说明父子折叠的影响:
1
2
3
4
5
<div style="margin: 2rem; background: lavender;">
<p style="margin: 0.4rem 0 1.2rem;">第一个段落</p>
<p style="margin: 0.8rem 0;">第二个段落</p>
</div>
<p>我离上面的 div 有 2rem 间距</p>
第二个 <p> 的 margin-bottom 会和父 <div> 的 margin-bottom 折叠,结果取最大值 max(0.8rem, 2rem) = 2rem。
Empty blocks(空块级元素)
如果一个块级元素的 margin-top 和 margin-bottom 之间没有任何 border、padding、inline 内容、height 或 min-height 来分隔,那么它自己的上下 margin 会折叠在一起。
1
2
<!-- 这个空 div 上下 margin 都是 20px,但它们会折叠 -->
<div style="margin-top: 20px; margin-bottom: 20px;"></div>
结果:这个空 div 整体只占用 20px 的 margin 空间,而不是 40px。
负 margin 的情况
- 正负混合:折叠后的 margin = 最大正值 + 最小负值(如
30px+-10px=20px) - 全部为负:折叠后的 margin = 最负的那个值
哪些容器不会产生折叠
display: flex 或 display: grid 的容器内部,margin 不会折叠。
阻止折叠的完整清单
以下条件任一满足即可阻止父子 margin 折叠:
| 阻止条件 | 适用方向 |
|---|---|
border-top / border-bottom | top / bottom |
padding-top / padding-bottom | top / bottom |
| 内有 inline 内容(文字等) | top |
显式 height / min-height | bottom |
创建新 BFC(如 overflow: hidden) | 均可 |
clear 产生的 clearance | top |
即使父元素的 margin 是
0,这些规则仍然生效:后代的 margin 有可能”穿透”父元素,跑到父元素外面。
Understanding and setting aspect ratios(理解与设置宽高比)
页面上的每个元素都有宽高,因此也就有一个 aspect ratio(宽高比)。媒体对象的”原始尺寸”——不经任何缩放、裁剪、边框影响时的大小——称为 intrinsic size(固有尺寸),由元素自身决定,不受 box sizing 或 border/margin/padding 影响。
开发中你经常希望设宽度为视口或父容器的百分比,然后让高度按比例自动变化。对 replaced elements(如图片、视频)来说,保持宽高比不只是响应式设计的需要,更直接关系到用户体验——如果不预留空间,媒体加载完成后页面会发生布局抖动(loading jank),因为之前没有给它留位子。
CSS 的
aspect-ratio属性可以同时调控 replaced 和 non-replaced 元素的宽高比。
How the aspect-ratio property works(aspect-ratio 属性是如何工作的)
aspect-ratio 定义元素盒子的首选(preferred)宽高比。值可以是 <ratio>、关键字 auto、或两者的组合。
<ratio> 由两个正数用 / 分隔表示,或直接用一个数(等价于 <number> / 1):
1
2
3
4
5
6
7
8
9
10
/* 以下全部等价 */
aspect-ratio: 3 / 6;
aspect-ratio: 1 / 2;
aspect-ratio: 0.5 / 1;
aspect-ratio: 0.5;
/* 这些也全部等价 */
aspect-ratio: 9/6;
aspect-ratio: 3/2;
aspect-ratio: 1.5;
auto 的行为取决于元素类型:
- 对 replaced elements 有固有宽高比的 →
auto= 使用固有宽高比 - 其他情况 →
auto= 没有首选宽高比(即默认行为)
当 auto 和 <ratio> 同时出现(如 aspect-ratio: auto 2/3):auto 用于有固有宽高比的 replaced elements,<ratio> 作为 fallback 首选宽高比。
⚠️ “首选”是关键:
aspect-ratio只在至少一个维度的尺寸是自动(auto)时才生效。如果 width 和 height 都显式设了值,aspect-ratio会被直接忽略。显式尺寸优先于宽高比。
对于 replaced elements,如果没有显式设尺寸(auto 不算),两个维度都会回到 intrinsic size,此时 aspect-ratio 也不会生效。
举些例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 /* ❌ 不生效——两维都设死了 */ .box { width: 300px; height: 200px; aspect-ratio: 1 / 1; /* 被忽略,盒子还是 300×200 */ } /* ✅ 只设了 width,高度由比例推出 = 300 × 3/4 = 225px */ .box { width: 300px; aspect-ratio: 4 / 3; } /* ✅ 图片设 auto 3/4——auto 走固有比例,3/4 是 fallback */ img { width: 300px; aspect-ratio: auto 3/4; }
Adjusting aspect ratios of replaced elements(调整替换元素的宽高比)
replaced elements(如 <img>、<video>)自带固有宽高比。如果你只设一个维度,浏览器会自动按原比例算另一个:
1
2
3
4
img {
width: 220px;
/* height 自动按图片比例计算 */
}
只有同时设了 width 和 height 时,才可能破坏宽高比:
1
2
3
4
5
img {
width: 100vw;
height: 100vh;
/* 图片被拉伸/挤压,惨不忍睹 */
}
用 aspect-ratio 也能产生同样的拉伸效果,只要你只设一个维度 + 给一个不等于原图比例的值。虽然一般你不会故意这么做,但要知道存在这种可能。
Fitting replaced elements within their containers(让替换元素适配容器)
想让 replaced element 匹配容器尺寸同时保持固有宽高比,应该用 object-fit:
| 值 | 效果 |
|---|---|
cover | 填满容器,裁剪超出的部分,保持比例 |
contain | 完整显示在容器内,可能留白,保持比例 |
fill(默认) | 拉伸填满,破坏比例 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
.grid-item {
aspect-ratio: 5 / 2; /* 容器自身的宽高比 */
}
.grid-item img {
width: 100%;
height: 100%;
object-fit: cover; /* 填满格子,裁剪多余 */
}
Defining aspect ratios for non-replaced elements(为非替换元素设置宽高比)
non-replaced elements(如 <div>、<p>)没有固有宽高比,尺寸由内容决定。同一段文字在 200px 和 600px 宽的容器里行数完全不同。
replaced element 的内容来自外部,CSS 控制的是它的”框”而不是内容本身;non-replaced element 的内容由 CSS 和 HTML 直接渲染。
以前想要固定宽高比只能同时设 width 和 height 再配合 overflow: hidden。现在 aspect-ratio 提供了显式的宽高比支持,即使你不知道内容或屏幕尺寸也能锁定比例:
1
2
3
4
blockquote {
inline-size: max-content;
aspect-ratio: 1; /* 正方形,height = width */
}
这里只设了 inline-size(在横排语言中就是 width),height 由 aspect-ratio: 1 自动推导出来。
对 non-replaced block-level 元素,其
inline-size默认就是容器的 content box 宽度。所以即使不显式设尺寸,aspect-ratio也能起作用。
基于容器尺寸创建圆形
1
2
3
4
5
6
7
8
9
10
11
12
13
14
div, p {
border-radius: 50%;
}
div {
width: 200px;
padding: 5px;
/* 椭圆形,因为 height 由内容撑开 */
}
p {
aspect-ratio: 1;
/* 正圆形!width 继承 div 的 content-box 宽度(190px),height 自动 = 190px */
}
non-replaced 元素的
inline-size默认等于容器 content box 宽,这个”自动有尺寸”的特性让aspect-ratio无需额外设定就能工作。
Common aspect-ratio use cases(常见用例)
让外部嵌入内容响应式
第三方嵌入(YouTube、TikTok、Instagram)通常返回 <iframe>,而 <iframe> 不像 <video> 那样能自动继承媒体文件的宽高比。
标准做法:给 iframe 容器设 aspect-ratio 匹配视频本身的比例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.youtube {
aspect-ratio: 16/9;
width: 100vw;
height: auto;
}
.tiktok,
.instagram {
aspect-ratio: 9/16;
height: 100vh;
width: auto;
}
/* 视口特别宽时,YouTube 改为占满高度 */
@media (aspect-ratio > 16/9) {
.youtube {
width: auto;
height: 100vh;
}
}
这样无论视口怎么变,视频比例永远正确,不会留黑边或裁掉关键内容。
但实际上iframe的内容显示是无法保证一定适配容器的。
iframe 可以理解成是HTML API,URL 传参,返回一个带独立 JS/DOM/事件循环的窗口。
不同的iframe返回可能有三种情况:
比例 黑边 大平台(YouTube) iframe在被返回时就已硬编码 16:9,平台内部做好自适应 无 自己平台的数据 后端传 ratio 字段,动态设 无 完全未知 只能猜比例,比如 16:9 很可能有,且不可单方消除 黑边本质是跨域限制,父页面不知道内部视频比例,CSS 当然无法协助。而大平台的iframe是 SDK 级封装,自己平台的靠前后端约定,陌生 iframe 就要听天由命了。
要点速记
| 场景 | 策略 |
|---|---|
| replaced elements 保持比例 | 只设一个维度,另一维度 auto |
| replaced elements 适配容器 | object-fit: cover / contain |
| non-replaced 元素锁定比例 | aspect-ratio + 一个维度尺寸 |
| 第三方嵌入(YouTube 等) | 给 iframe 容器设 aspect-ratio |
| 同时设了 width 和 height | aspect-ratio 被忽略 |
aspect-ratio是一个”首选”值,不是强制执行。当显式尺寸与它冲突时,显式尺寸优先。它的价值在于:你不需要知道内容能撑多高,只需要声明”我就要这个比例”。