用一个登录卡片讲清 CSS 盒模型:padding、border 与 box-sizing 到底如何影响布局
登录卡片是理解 CSS 盒模型的好例子:它既有固定宽度的外框,也有需要撑满可用空间的输入框和按钮。
很多布局问题都长得像这样:
明明给输入框写了
width: 100%,为什么它还是比容器更宽?
答案通常不在 Flexbox 或 Grid,而在盒模型:你设置的 width 到底是在约束内容区,还是在约束包含边框的外框?
本文只围绕一个登录卡片,弄清四件事:
padding、border、margin分别位于哪里;- 默认的
content-box如何计算尺寸; border-box改变了什么,又没有改变什么;- 为什么表单控件配合
width: 100%时,通常需要明确盒模型规则。
先看问题:width: 100% 为什么还能变宽?
假设登录卡片中有输入框:
html
<div class="login-card">
<h1>欢迎回来</h1>
<label class="field">
<span>账号</span>
<input type="text" placeholder="name@example.com" />
</label>
<label class="field">
<span>密码</span>
<input type="password" placeholder="输入密码" />
</label>
<button type="button">登录</button>
</div>
先故意使用 content-box:
css
.login-card {
box-sizing: content-box;
width: 320px;
padding: 24px;
border: 1px solid #d9e0ea;
border-radius: 12px;
}
.field {
display: block;
margin-top: 16px;
}
.field input,
.login-card button {
box-sizing: content-box;
width: 100%;
padding: 12px;
border: 1px solid #aeb9c8;
border-radius: 8px;
}
这里的关键是:在 content-box 下,width: 100% 只约束内容区。
.login-card 的内容区宽度是 320px。输入框的百分比宽度以其包含块的内容区为基准,因此输入框内容区也是 320px。加上左右内边距和边框后,输入框的边框盒宽度为:
text
320px(内容区)
+ 12px(左 padding)
+ 12px(右 padding)
+ 1px(左 border)
+ 1px(右 border)
= 346px
也就是说,输入框相对于卡片的 320px 内容区多出了 26px。它会先占用卡片右侧的部分 padding;如果父元素的空间不足或存在其他约束,便可能越过卡片边框,形成横向溢出。
这就是"width: 100% 却没有刚好填满"的根源:100% 不一定指元素最终外框的 100%。
登录卡片的四层:content、padding、border、margin
一个普通块级盒子可以从内到外理解为四层:
- content box(内容区) :标题、文字、表单内容实际放置的区域;在默认盒模型下,
width和height描述的就是它。 - padding(内边距) :内容与边框之间的留白。登录卡片的
padding: 24px,就是让表单不要紧贴卡片边线。 - border(边框) :围住 padding 的可见边线,例如
1px solid #d9e0ea。 - margin(外边距):位于边框之外,用于拉开卡片与页面或相邻组件之间的距离。

要特别区分两件事:
padding是卡片内部的留白;margin是卡片外部与其他元素的间距。
从盒模型结构看,margin 位于 border 之外。它会影响元素在页面排布时占用的外部空间,但不属于元素的 border box,也不属于卡片自身带边框的可见外框。
默认规则:content-box 会把 padding 和 border 加到外面
box-sizing 的初始值是:
css
box-sizing: content-box;
在这个规则下,声明的 width 和 height 对应内容区;padding 与 border 会额外增加到最终的 border box 尺寸上。
继续使用登录卡片的数字:
css
.login-card {
box-sizing: content-box;
width: 320px;
padding-inline: 24px;
border-inline: 1px solid #d9e0ea;
}
它的尺寸关系是:
text
声明 width:320px
内容区宽度:320px
最终 border box 宽度:
320 + 24 + 24 + 1 + 1 = 370px
因此,width: 320px 并不等于"你在屏幕上看到的卡片边框外沿是 320px 宽"。在 content-box 下,它表示:卡片内部可放内容的区域为 320px 宽。
如果再写:
css
margin-inline: auto;
margin-top: 48px;
这些 margin 不会计入上面的 370px border box 宽度;但它们会影响卡片与页面边缘、其他组件之间的距离。需要注意的是,margin-inline: auto 的实际分配效果还取决于包含块是否存在可分配的剩余空间。
换成 border-box:320px 就是卡片的边框盒宽度
现在只改一行:
css
.login-card {
box-sizing: border-box;
width: 320px;
padding-inline: 24px;
border-inline: 1px solid #d9e0ea;
}
在 border-box 下,width 包含:
text
content + 左右 padding + 左右 border
所以这次:
text
声明 width:320px
最终 border box 宽度:320px
内容区宽度:
320 - 24 - 24 - 1 - 1 = 270px
也就是说,border-box 不是让 padding 和 border 消失,而是把它们纳入已经声明的尺寸预算。为了容纳内边距和边框,内容区会相应缩小。
这也是为什么做固定宽度卡片时,很多人更喜欢 border-box:当你写 width: 320px,在没有其他尺寸约束介入时,卡片的 border box 宽度就是 320px。
同一个输入框,两种计算方式
下面把输入框单独拿出来比较。假设它所在包含块的内容区宽度为 270px。
情况一:输入框使用 content-box
css
input {
box-sizing: content-box;
width: 100%;
padding-inline: 12px;
border: 1px solid #aeb9c8;
}
计算结果:
text
width: 100% → 内容区为 270px
最终 border box 宽度:
270 + 12 + 12 + 1 + 1 = 296px
输入框的边框盒宽度比可用内容区域多 26px。它可能先占用父元素右侧的 padding;padding 不够时,就会越过父元素的边框区域。
情况二:输入框使用 border-box
css
input {
box-sizing: border-box;
width: 100%;
padding-inline: 12px;
border: 1px solid #aeb9c8;
}
计算结果:
text
width: 100% → border box 为 270px
内容区宽度:
270 - 12 - 12 - 1 - 1 = 244px
这次输入框的外框刚好是 270px,与父元素的可用内容宽度对齐。对于"卡片内表单控件占满一行"这种常见需求,border-box 通常更符合直觉。
百分比宽度以包含块的相应尺寸为基准解析;而 box-sizing 决定声明的宽度最终应用于 content box 还是 border box。
一份可运行的登录卡片对比代码
下面的示例把两种盒模型并排展示。两张卡片的 CSS 声明宽度都为 320px,但最终边框盒宽度和内部控件表现不同。
html
<div class="login-demos">
<section class="login-card content-demo">
<h2>content-box</h2>
<label class="field">
<span>账号</span>
<input type="text" placeholder="name@example.com" />
</label>
<label class="field">
<span>密码</span>
<input type="password" placeholder="输入密码" />
</label>
<button type="button">登录</button>
</section>
<section class="login-card border-demo">
<h2>border-box</h2>
<label class="field">
<span>账号</span>
<input type="text" placeholder="name@example.com" />
</label>
<label class="field">
<span>密码</span>
<input type="password" placeholder="输入密码" />
</label>
<button type="button">登录</button>
</section>
</div>
css
.login-demos {
display: flex;
flex-wrap: wrap;
gap: 32px;
padding: 32px;
background: #f5f7fb;
}
.login-card {
width: 320px;
padding: 24px;
border: 1px solid #d9e0ea;
border-radius: 12px;
background: #fff;
font: 14px/1.5 system-ui, sans-serif;
}
.content-demo {
box-sizing: content-box;
}
.border-demo {
box-sizing: border-box;
}
.field {
display: block;
margin-top: 16px;
}
.field span {
display: block;
margin-bottom: 6px;
}
/* 故意保留 content-box,观察 width: 100% 的额外尺寸 */
.login-card input,
.login-card button {
box-sizing: content-box;
display: block;
width: 100%;
padding: 12px;
border: 1px solid #aeb9c8;
border-radius: 8px;
font: inherit;
}
.login-card button {
margin-top: 20px;
cursor: pointer;
color: #fff;
border-color: #2563eb;
background: #2563eb;
}
/* 推荐修复:让控件以 border box 对齐可用宽度 */
.border-demo input,
.border-demo button {
box-sizing: border-box;
}
观察时注意两点:
.content-demo的width: 320px指内容区,因此它的 border box 宽度实际为370px;.border-demo的width: 320px指 border box,而其中的输入框和按钮也使用border-box,所以width: 100%对应它们完整的 border box 宽度。
不要把问题简单归因于"输入框有 padding"。真正决定结果的是:padding 和 border 是否被包含在声明宽度之内。
height 也遵循同一套规则
宽度如此,高度也是如此:
css
input {
height: 44px;
padding-block: 12px;
border: 1px solid;
}
- 在
content-box下,height: 44px是内容区高度,最终边框盒高度还要加上上下 padding 和上下 border; - 在
border-box下,44px是输入框包含边框在内的 border box 高度,内容区会被扣减。
不过,表单控件的视觉高度不只由 box-sizing 决定。文字的 line-height、字体、浏览器默认样式,以及原生控件的渲染方式,都可能影响最终观感。
做精确表单 UI 时,建议显式设置 font、line-height、padding、border 和 box-sizing,不要完全依赖浏览器默认外观。虽然 box-sizing 的初始值是 content-box,但浏览器的用户代理样式表可能会为部分表单控件或特定 input 类型设置 border-box;实际项目中应以 DevTools 的计算后样式为准。
实战推荐:统一采用可继承的 border-box
项目中常见的基础规则是:
css
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
它的作用是:
- 让页面元素默认使用
border-box; - 让伪元素
::before、::after也遵循同一规则; - 在固定宽度、百分比宽度的卡片、输入框、按钮中,减少"还要手工减去 padding 和 border"的计算;
- 保留局部覆盖的余地:某个特殊组件仍可以单独写
box-sizing: content-box。
但它不会做以下事情:
- 不会把
margin计入width或height; - 不会自动分配 Flexbox 或 Grid 中的剩余空间;
- 不会修复由
min-width、长文本、不换行内容或绝对定位造成的所有溢出; - 不会消除 padding、border,只会改变它们参与尺寸计算的方式。
用 DevTools 验证,而不是靠猜
当你发现登录卡片尺寸不对时,可以在浏览器开发者工具中检查目标元素:
- 选中卡片、输入框或按钮;
- 打开样式面板,确认实际生效的
box-sizing; - 查看 Box Model 图,分别检查 content、padding、border、margin 的数值;
- 对比声明的
width与实际 border box 宽度; - 临时切换
content-box和border-box,观察内容区与外框如何变化。
这比盲目给元素加 overflow: hidden、减少宽度或删除 padding 更可靠。
结论:把 width 理解成一份尺寸预算
用登录卡片记住下面这组判断即可:
padding:内容到边框的内部留白;border:卡片或控件的可见边线;margin:元素与外界的距离,不属于 border box;content-box:width只给内容区,padding 和 border 额外增加;border-box:width给 border box,padding 和 border 从中扣除;width: 100%并不天然表示"最终外框刚好占满 100%",还要看元素的box-sizing;- 对登录卡片、输入框、按钮这类强调对齐的 UI,统一采用
border-box往往更容易控制布局。
当你下一次看到"输入框明明是 100%,却把卡片撑宽了",先不要怀疑布局系统。先问一句:这 100% 指的是 content box,还是 border box?