本节摘要:默认盒模型有个著名的坑——设置了
width: 200px的元素实际占宽不止 200px,内边距和边框都会额外撑大它。box-sizing属性让你选择尺寸的计算口径,border-box模式让宽度"所见即所得",是现代项目全局重置的标配。本节动手复现这个坑、给出解法,并补齐约束属性与视口单位两件弹性尺寸工具。
完成本节后,你将能够:动手复现 content-box 撑大元素的问题;解释 border-box 为什么是现代项目标配并写出全局重置;用 max-width/min-height 等约束实现自适应尺寸;区分 vw/vh/vmin/dvh 的适用场景。
不谈理论,直接把坑踩一遍。写两个并排的盒子,都声明 width: 200px:
<div class="row"> <div class="box-a">A</div> <div class="box-b">B</div> </div>
.row { font-size: 0; } /* 消除行内间隙,第4章会讲 */ .box-a { box-sizing: content-box; /* 浏览器默认 */ width: 200px; padding: 20px; border: 2px solid #333; display: inline-block; background: #FFCDD2; } .box-b { box-sizing: border-box; width: 200px; padding: 20px; border: 2px solid #333; display: inline-block; background: #C8E6C9; }
刷新页面:两个盒子宽度不一样。红色的 A 明显比绿色的 B 宽——用开发者工具量一下,A 是 244px,B 是 200px。同样的 width: 200px,两种命运,差别只在 box-sizing。
CSS 提供了两种盒模型尺寸计算口径,通过 box-sizing 属性切换。
content-box 是 CSS 规范的默认行为。在这种模式下,width 和 height 只控制内容区的尺寸,内边距和边框会额外增加元素的总占位。
.box { box-sizing: content-box; /* 默认值,可以不写 */ width: 200px; padding: 20px; border: 2px solid #333; } /* 实际总宽度 = 200 + 20×2 + 2×2 = 244px */ /* 不是你以为的 200px! */
这意味着如果你在一个 600px 宽的容器里放了三个 width: 200px 的盒子并给它们加了内边距,三个盒子会溢出容器——因为每个盒子的实际宽度都是 244px,总计 732px。想不溢出,你得心算"200 - 20×2 - 2×2 = 156"然后把宽度写成 156px。每改一次内边距就重算一次,这是上世纪前端们的日常。
border-box 让 width 和 height 包含内容、内边距和边框。外边距仍然额外计算。
.box { box-sizing: border-box; width: 200px; padding: 20px; border: 2px solid #333; } /* 总宽度 = 200px(内边距和边框包含在内) */ /* 内容区自动缩小为 200 - 20×2 - 2×2 = 156px */
两种盒模型对同一个声明的不同解读
border-box 之所以成为现代 CSS 开发的标配,是因为它让尺寸计算变得直觉化。当你说"这个卡片宽 300px"时,你期望的就是它总共占 300px 的宽度——不管里面有多少内边距和边框。设计稿上量出来的尺寸直接照抄即可,不用再做加减法。
几乎所有主流 CSS 框架都在全局重置中设置了 border-box:
/* 现代 CSS 项目的标准开场 */ *, *::before, *::after { box-sizing: border-box; }
注意这里不仅重置了 *,还包含了 *::before 和 *::after——确保伪元素也使用 border-box,避免装饰性元素的尺寸计算出现意外(第 2 章那些用伪元素画的角标、下划线,都受益于这行)。
| 对比维度 | content-box | border-box |
|---|---|---|
width 包含 |
仅内容区 | 内容 + padding + border |
| 加 padding 后 | 元素变大 | 内容区缩小,元素不变 |
| 直觉程度 | 反直觉 | 符合预期 |
| 布局计算 | 需要手动加减 | 直接设置即可 |
| 使用频率 | 几乎不用 | 现代项目标配 |
border-box 的价值在百分比布局里最明显。做三组三栏对比,容器 100% 宽:
.cols { display: flex; gap: 0; } /* 第4章预告:先借用 flex 排列 */ /* 第一组:content-box + 百分比 —— 灾难 */ .bad .col { box-sizing: content-box; width: 33.33%; padding: 16px; } /* 每栏实际占 33.33% + 32px,三栏必然溢出换行 */ /* 第二组:border-box + 百分比 —— 正常 */ .good .col { box-sizing: border-box; width: 33.33%; padding: 16px; } /* 三栏恰好占满,内边距在栏内消化 */
第一组刷新后你会看到第三栏被挤到下一行——这就是没做全局重置时最典型的"布局莫名散架"。第二组严丝合缝。把全局重置那三行加到样式文件最顶上,从此这类问题与你绝缘。
再做一个"所见即所得"的验证:给一张卡片声明 width: 100%; max-width: 340px; padding: 24px; border: 1px solid #ddd,在 border-box 下无论怎么改内边距,卡片在页面上量出来永远是 340px——改样式不再牵动布局,这就是口径统一带来的稳定性。
除了 width 和 height,CSS 还提供了一组约束属性,让元素的尺寸可以根据内容自适应。
.responsive-card { width: 100%; /* 撑满父容器 */ max-width: 400px; /* 但不超过 400px */ min-height: 200px; /* 至少 200px 高 */ padding: 20px; box-sizing: border-box; }
| 属性 | 作用 | 典型用途 |
|---|---|---|
max-width |
最大宽度 | 防止内容过宽影响阅读 |
min-width |
最小宽度 | 保证按钮不会太窄 |
max-height |
最大高度 | 限制图片/视频区域高度 |
min-height |
最小高度 | 确保容器至少有一定高度 |
一个常见的模式是用 max-width 配合 margin: auto 来创建居中且不超过某个宽度的内容区域:
.container { max-width: 1200px; margin: 0 auto; /* 水平居中 */ padding: 0 16px; /* 小屏幕上留出左右间距 */ box-sizing: border-box; }
这四行是响应式内容的"标准容器"写法:大屏时内容区最宽 1200px 居中,小屏时收缩为满宽并保留 16px 边距。width: 100% 都不必写——块级元素默认就占满可用宽度,max-width 负责封顶。配合 min-width 还能防极端:min-width: 280px 保证容器不会被压到失去可读性。
约束属性的思维转变在于:不再精确声明尺寸,而是给尺寸画一个护栏。内容少就收缩,内容多就到护栏为止,这种"弹性声明"正是响应式布局的思想内核——第 4 章的 fr 单位、minmax() 函数都是这一思想在网格里的延伸。
CSS 提供了基于浏览器视口(viewport)尺寸的单位,适合做全屏布局:
.hero { height: 100vh; /* 视口高度的 100%,满屏首屏 */ width: 100vw; /* 视口宽度的 100% */ } .sidebar { width: 30vw; /* 视口宽度的 30% */ min-height: 100vh; /* 至少满屏高 */ }
| 单位 | 含义 | 注意 |
|---|---|---|
vw |
视口宽度的 1% | 包含滚动条宽度 |
vh |
视口高度的 1% | 移动端受地址栏影响 |
vmin |
vw 和 vh 中较小的 | 适合保证内容可见 |
vmax |
vw 和 vh 中较大的 | 适合全屏覆盖效果 |
dvh |
动态视口高度 | 移动端全屏的现代方案 |
视口单位与 border-box 组合有个经典陷阱:width: 100vw 会让元素比可视区域宽一点——因为 100vw 把滚动条宽度也算进去了,产生横向滚动条。全宽区块用 width: 100% 更稳,vw 留给确实需要跟视口联动的场景。
💡 动手建议:在你的样式文件最开头加上
*, *::before, *::after { box-sizing: border-box; },然后正常写布局代码。你会发现再也不需要手动计算"宽度减去内边距"了。改完之后回头看看之前写的固定宽度有没有"悄悄变宽"的,统一修正一遍——这是每个 CSS 项目早期都值得做的一次"口径统一"。
⚠️
100vh在移动端浏览器中可能不等于屏幕的实际可见高度,因为移动浏览器的地址栏会占据一部分空间,滚动时地址栏还会收起或展开,导致100vh的元素忽高忽低。如果需要精确的全屏高度,使用dvh(动态视口高度),它在地址栏变化时实时跟随。

图:content-box 与 border-box 的尺寸计算差异
问:全局重置会不会影响第三方组件的样式? 理论上会改变那些依赖默认 content-box 行为的组件的尺寸计算,但主流组件库和框架自身就按 border-box 体系设计,冲突极少。真正需要留心的是直接复制来的零散代码片段——它们可能假设了默认口径。统一口径永远是利大于弊,出了问题对症改那一段即可。
问:width: 100% 加上内边距为什么还是溢出? 在 content-box 下,100% 指内容区占满父容器,内边距和边框再往外叠加,必然溢出。两种解法:开 border-box(推荐);或改用 width: auto 让块级元素自动填满可用宽度且不受内边距影响。这也是"为什么全局重置是第一行代码"的最好注脚——不踩过这个坑,很难体会它的价值。
问:max-width 和 width 同时写,谁说了算? 各管一端:width 提议,max-width 否决超限,min-width 否决过小。三者并存时的结果可以概括为"宽度先取 width,再被夹在 min 与 max 之间"。所以响应式容器的经典写法是"width 满宽、max-width 封顶",而不是来回改 width 本身。
问:vh 单位的页面为什么手机上底部被截掉了? 移动浏览器的地址栏占据一部分高度,100vh 按的是"地址栏收起后"的大视口,实际可见区域更小。方案:用 100dvh(动态视口,随地址栏伸缩);或用 min-height 替代固定 height 给内容留活路;或干脆用普通文档流让页面自然延展,全屏区块仅用于确实需要满屏的展示场景。
问:什么时候必须显式写 content-box? 几乎只有一种情况:你在还原一段依赖默认口径的老代码,且暂时不能改动它。日常新代码中,显式写 content-box 更多是"此地无银"式的自我暴露——看到它就该警觉这段代码的历史包袱。
尺寸控制这条线,其实藏着响应式思维的完整台阶。第一级是"固定尺寸":宽三百像素的卡片,桌面端很好,手机上溢出——这是初学者的起点,也是问题所在。第二级是"护栏尺寸":max-width 与 min-width 给盒子画上下界,内容在小屏收缩、大屏封顶,上一节的标准容器就是这一级的代表,它已经能应付内容型页面的多数场景。
第三级是"弹性分配":不再声明盒子的绝对大小,而是声明它如何在群体中分蛋糕——Flex 的 flex: 1 与 Grid 的 fr 都属此列,第四章会正式展开。三级台阶的共同内核是:从"告诉浏览器尺寸是多少",进化为"告诉浏览器尺寸的规则是什么"。规则比数值更适应未知屏幕宽度,这正是响应式设计与其说是一组技术、不如说是一种思维转变的原因。
顺带把宽度相关的一组属性做个总复习:width 是期望值,min-width 与 max-width 是护栏,box-sizing 决定数值的口径,百分比单位决定参照物,fr 与 flex 则把分配权交给容器。把这几个旋钮的因果关系想通,遇到任何"宽度不合预期"的问题,你都能沿着"期望值、口径、参照物、护栏、分配"这条链逐环排查——这是一条可以重复使用的排查路径,比碰运气强太多。
还有一个综合小实验可以把本章两节串起来:做一个"自适应新闻卡片"。要求是——窄屏时占满容器、宽屏时封顶四百像素,最小高度保证标题加两行摘要放得下,内边距统一舒适,边框一像素圆角适度。把你脑子里浮现的代码与下面对照:
.news-card { box-sizing: border-box; width: 100%; max-width: 400px; min-height: 180px; padding: 20px; border: 1px solid #E0E0E0; border-radius: 8px; margin-bottom: 16px; }
八行声明里,口径、期望值、护栏、身体尺寸全部各就各位——这正是"盒模型思维"的完整体检:看到任何一个尺寸类属性,你都能立刻说出它的口径是什么、参照物是谁、上下界在哪。做完了还可以再加一步压力测试:故意把最大宽度改成三百像素、最小高度改成两百四十像素,刷新观察卡片的收缩与撑高行为是否与你的预判一致。能对任意元素的这八个属性脱口而出解释的人,布局问题基本困不住他。
content-box 下 width 只管内容区,内边距和边框额外撑大元素,百分比布局必然溢出。border-box 让声明宽度即总宽度,配合伪元素一起重置,是每个项目的第一行代码。max-width/min-height 给尺寸画上下界,max-width 加 margin: auto 是标准内容容器。vw/vh 跟视口联动,dvh 解决移动端地址栏问题;全宽场景用 100% 避开滚动条陷阱。盒子的事到此讲完。下一章进入 CSS 最有成就感的领域——布局:让成百上千个盒子在页面上各就各位。