The problem
You have a container 400px wide and want a box inside it to fill the width, with a little padding. So you write:
.box { width: 100%; padding: 20px; border: 2px solid; }…and the box sticks out of its parent by 44px. You did nothing wrong; you ran into how CSS measures boxes by default.
content-box: the default
By default, width and height describe only the content area. Padding and border are added on top. This is called box-sizing: content-box:
visible width = width + padding-left + padding-right + border-left + border-rightSo a width: 300px box with 20px padding and a 2px border is 300 + 40 + 4 = 344px wide. Try it:
<style>
.box {
width: 300px;
padding: 20px;
border: 2px solid #7c3aed;
background: #ede9fe;
margin-bottom: 12px;
}
.border-box { box-sizing: border-box; }
</style>
<div class="box">content-box (default): 344px wide in total</div>
<div class="box border-box">border-box: exactly 300px wide</div>The second box is exactly as wide as you asked for. The text area inside it shrinks to make room for the padding and border.
border-box: what you actually want
With box-sizing: border-box, width and height include the content, the padding and the border:
visible width = width (always)
content width = width - padding - borderThat matches how people think: "this card is 300 pixels wide." Add padding or a border later and the card stays 300px. A column that is 50% wide with padding stays 50% wide.
Margin is outside both models and is never included.
The reset almost every project uses
Rather than set it on one box at a time, apply it to everything at the top of the stylesheet:
*, *::before, *::after {
box-sizing: border-box;
}The * selector matches every element, and the two pseudo-elements are included because * does not match them. Put this first, before other rules. A slightly more flexible version sets html { box-sizing: border-box; } and *, *::before, *::after { box-sizing: inherit; }, which lets a third-party widget opt out. Either is fine.
Try the fix in a realistic case:
<style>
* { box-sizing: border-box; }
.wrapper { width: 300px; border: 2px dashed #6b7280; padding: 0; }
.inner { width: 100%; padding: 20px; background: #dcfce7; }
</style>
<div class="wrapper">
<div class="inner">100% wide with padding: no overflow now.</div>
</div>Remove the first rule and watch .inner spill out of the dashed box.
What it does and does not change
- It changes how
width,height,min-*andmax-*are measured. - It does not change padding, border or margin themselves.
- Form controls (inputs, buttons, selects) already use
border-boxin most browsers, which is part of why people can feel they behave differently fromdivboxes. - Images and other replaced elements follow the same rule.
Why this became the standard
Early on, designers did sums to keep layouts consistent: "the column is 300 minus 40 padding minus 4 border, so I set 256." Switching to border-box removes the sums and makes percentages behave. It is one of the first lines in nearly every CSS framework and reset, and in this site's own stylesheet.
Common mistakes
- Forgetting the reset and chasing overflow bugs with random negative margins.
- Applying
border-boxonly to some elements, giving inconsistent sizes. - Expecting margin to be included.
- Using
width: 100%with horizontal margin, which still overflows. Use auto width on blocks where you can. - Mixing both models in one layout without realising it.
Exercise
Make two boxes sit in a 600px-wide container, each 50% wide, with 20px padding and a 2px border. They should fit side by side with no wrapping. (Hint: use display: inline-block or float, plus the reset.)
Show one possible answer
<style>
* { box-sizing: border-box; }
.row { width: 600px; font-size: 0; }
.col { display: inline-block; width: 50%; padding: 20px; border: 2px solid #4f46e5; font-size: 16px; vertical-align: top; }
</style>
<div class="row">
<div class="col">Left</div><div class="col">Right</div>
</div>font-size: 0 on the row removes the small gap that whitespace between inline-block boxes creates. You will see cleaner ways with Flexbox soon.
Key points
- By default (content-box), width and height set only the content area, so padding and border make the box bigger.
- With border-box, width and height include padding and border, so the box is exactly the size you set.
- Setting a width of 100 percent plus padding with content-box overflows its parent, which is a very common bug.
- Most stylesheets start with a rule that sets border-box on every element.
- Margin is never included in either model.