You set width: 300px on a box, add some padding so the text isn’t cramped, give it a border so it stands out — and suddenly the box is wider than 300px and breaks out of its column. You didn’t change the width. So what happened?
The answer is box-sizing, one of the most quietly important properties in all of CSS. It controls a single question: when you say width: 300px, does that 300 include the padding and border, or not? Get this one property right and a whole class of “why is my layout broken” problems simply disappears. This article goes deep on how it works, the difference between its two values, and why nearly every modern stylesheet sets it the same way on line one.
If you’ve already read about the CSS box model, this is the natural follow-up — the box model tells you a box has content, padding, border, and margin; box-sizing tells you which of those layers your width and height actually measure.
A quick recap: the four layers of a box
Every element in CSS is a rectangular box made of four layers, from the inside out:
- Content — the text or image itself, sized by
widthandheight. - Padding — space inside the box, between the content and the border.
- Border — the line around the padding.
- Margin — space outside the box, separating it from neighbors.
The question box-sizing answers is about the first three. Specifically: when the browser calculates how much horizontal space your box occupies, does width refer to just the content, or to the content plus padding plus border? That single decision is the whole story.
Margin is never part of box-sizing
No matter what value you choose, box-sizing never includes the margin. Margin is space outside the box, so it’s always added on top of whatever width the box ends up being. box-sizing only ever changes how padding and border are counted — never margin.
content-box: the original (and confusing) default
The default value of box-sizing is content-box. With it, width sets the size of the content area only. Padding and border are then added outside that width, making the rendered box bigger than the number you typed.
Here’s the classic surprise:
.box {
box-sizing: content-box; /* the default */
width: 300px;
padding: 20px;
border: 5px solid #00838f;
}
You wrote width: 300px, but the box that actually appears on screen is wider than that. The browser does the math like this:
300 (content) + 20 + 20 (left/right padding) + 5 + 5 (left/right border) = 350px total
So the visible box is 350px wide, not 300px. The same logic applies vertically to height. The width you set is only the innermost layer; everything else gets stacked around it.
This behavior isn’t a bug — it’s how the original CSS specification defined things, and it’s been the default for over two decades. But it’s deeply counterintuitive. When most people write width: 300px, they mean “I want this box to be 300px wide,” not “I want the content to be 300px and then please add an unknown amount on top.” That mismatch between intention and behavior is exactly what border-box was created to fix.
border-box: the value you actually want
Set box-sizing: border-box and the meaning of width flips to something far more sensible: width now includes the padding and border. The total visible box matches the number you wrote, and the content area shrinks to make room for the padding and border inside it.
.box {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 5px solid #00838f;
/* The box is EXACTLY 300px wide. Padding and border fit inside. */
}
Same padding, same border — but now the box is exactly 300px wide, period. The browser starts from your 300px and carves the padding and border out of the inside:
300 (total) − 20 − 20 (padding) − 5 − 5 (border) = 250px of content
So the content area becomes 250px while the box on screen stays at your intended 300px. This is almost always what you want. You set a width, you get that width — full stop. The padding and border live inside the budget instead of blowing it.
border-box is the modern default for a reason
With border-box, width means “the total visible width of the box.” That’s how nearly everyone intuitively expects sizing to work, which is why modern CSS projects switch to it globally. You stop fighting unexpected overflow, and width: 100% finally behaves predictably even when there’s padding. It’s hard to overstate how much friction this one value removes.
Seeing the difference side by side
The two values are easiest to grasp when you compare identical boxes. Both of these have width: 200px, padding: 20px, and a 10px border — the only difference is box-sizing:
.content-box {
box-sizing: content-box;
width: 200px;
padding: 20px;
border: 10px solid #ccc;
/* Rendered width: 200 + 40 + 20 = 260px */
}
.border-box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 10px solid #00838f;
/* Rendered width: exactly 200px (content area is 140px) */
}
The content-box version balloons to 260px; the border-box version stays at 200px. Put two such boxes next to each other in a fixed-width row and the difference becomes obvious — one fits, the other overflows. Once you’ve seen it, you’ll never want content-box back.
Why this matters most: width: 100% with padding
The single most painful place content-box bites you is when an element should fill its container and has padding. Think of a full-width input field, or a card that stretches across its column:
.field {
width: 100%;
padding: 12px;
border: 1px solid #ccc;
}
With the default content-box, this input is wider than its container. The 100% means “100% of the parent’s width for the content,” and then the 12px of padding on each side plus the border get added on top — so the input pokes out past the edge and often triggers a horizontal scrollbar. It’s one of the most common “my layout has a mysterious sideways scroll” bugs there is.
Switch to border-box and the problem vanishes:
.field {
box-sizing: border-box;
width: 100%;
padding: 12px;
border: 1px solid #ccc;
/* Now it fills the container exactly, padding and all. */
}
Now width: 100% truly means “fill the container,” and the padding is absorbed inside that 100% instead of being tacked on outside it. The input sits flush within its column, no overflow, no rogue scrollbar. This single scenario is reason enough to adopt border-box everywhere.
A stray horizontal scrollbar? Suspect box-sizing first
If your page has an annoying horizontal scrollbar you can’t explain, a width: 100% element with padding under the default content-box is the usual culprit. The element is overflowing its parent by exactly the padding-plus-border amount. Applying border-box is the fix the vast majority of the time.
The one-line fix every project uses
Because border-box is so much more intuitive, the standard practice is to apply it to everything at the very top of your stylesheet using the universal selector:
* {
box-sizing: border-box;
}
The * matches every element on the page, so this single rule makes the whole document use the sensible sizing model. You’ll find some version of this line at the top of virtually every modern CSS file, framework, and reset.
A slightly more robust version also covers pseudo-elements and lets components opt out cleanly:
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
Here you set border-box once on the <html> element, then tell every element (and the ::before / ::after pseudo-elements) to inherit that value. The advantage is flexibility: if a specific component needs content-box for some reason, you set it on that component and its children inherit it, rather than having a hardcoded border-box fight you. For most projects the simple * { box-sizing: border-box; } is perfectly fine — but it’s worth knowing the inherit pattern exists.
How box-sizing affects height
Everything so far has talked about width, but box-sizing applies to height in exactly the same way. With content-box, height sets only the content’s height and padding/border are added on top. With border-box, height includes the padding and border, so the box’s total height matches the number you set:
.banner {
box-sizing: border-box;
height: 80px;
padding: 16px;
border-top: 4px solid #00838f;
/* Total height stays 80px — content area is 80 - 16 - 16 - 4 = 44px */
}
This is handy whenever you need an element to be an exact height — a fixed header, a button row, an avatar — while still giving it internal padding. With border-box, the height you write is the height you get, and the padding simply eats into the space available for content.
What about padding-box?
You may stumble across a third value, padding-box, in old articles. It made width include the padding but not the border. In practice almost no one used it, and it has been removed from browsers — only content-box and border-box exist today. So you only ever need to think about two values: the confusing default (content-box) and the one you almost always want (border-box).
When would you ever keep content-box?
Given everything above, is there any reason to leave an element on content-box? Honestly, rarely — but a couple of cases come up:
- Some image or media sizing, where you specifically want the declared
width/heightto be the content size and the padding to extend the visible frame around it. - Integrating with third-party widgets or older code that was written assuming
content-boxmath, where flipping it would break their internal calculations.
These are edge cases. For your own layout work — cards, inputs, grids, columns, components — border-box is the right call essentially every time. The rare exception can opt out locally with box-sizing: content-box on just that element.
Putting it together: a card that behaves
Here’s a small, realistic example showing how border-box keeps a padded, bordered card neatly inside a fixed column. The card is meant to be exactly 320px wide, with comfortable internal padding:
* {
box-sizing: border-box;
}
.card {
width: 320px;
padding: 24px;
border: 1px solid #d0d7de;
border-radius: 12px;
}
<div class="card">
<h3>ACY Partner Indonesia</h3>
<p>A card that stays exactly 320px wide, padding and border included.</p>
</div>
Because border-box is in effect, this card is precisely 320px wide on screen — the 24px of padding and the 1px border sit inside that 320px, not on top of it. Drop several of these into a flex or grid layout and they line up perfectly, with no surprise overflow. Without border-box, every card would silently be 370px wide and your tidy grid would fall apart. (For arranging cards into rows and columns, see CSS Flexbox and CSS Grid.)
Wrapping up
box-sizing is a tiny property with an outsized impact on how predictable your layouts feel. Here’s what to carry forward:
box-sizingdecides whetherwidthandheightinclude padding and border. It never includes margin.content-box(the old default) measures only the content — padding and border are added on top, so boxes come out bigger than the width you set.border-boxmeasures the whole box — padding and border fit inside the width, so the box is exactly as wide as you asked.border-boxmakeswidth: 100%with padding behave correctly and kills the mysterious horizontal-scrollbar bug.- Apply it everywhere with
* { box-sizing: border-box; }at the top of your stylesheet — or use thebox-sizing: inheritpattern for opt-out flexibility. box-sizingaffectsheightthe same way it affectswidth.- The old
padding-boxvalue is gone; onlycontent-boxandborder-boxremain.
Master this one property and a lot of CSS layout pain quietly evaporates. Sizing becomes intuitive, padding stops breaking your widths, and width: 100% finally does what its name promises. From here, you’re well equipped to build layouts that hold their shape — and to debug the ones that don’t.