What you'll learn
Quick Answer
Every element is a box with four layers: content, then padding, then border, then margin. By default the width property sets only the content width, so padding and border are added on top and the element ends up wider than the number you wrote. Setting box-sizing: border-box makes width include padding and border, which is what almost everyone expects, and applying it globally is standard practice.
The Four Layers
From the inside out:
┌─────────────────────────────────────┐
│ margin (outside) │
│ ┌───────────────────────────────┐ │
│ │ border │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ padding │ │ │
│ │ │ ┌───────────────────┐ │ │ │
│ │ │ │ content │ │ │ │
│ │ │ └───────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘Content — the text or child elements.
Padding — space inside the border. Takes the element's background.
Border — the visible edge.
Margin — space outside the border, always transparent.
The practical distinction between padding and margin: padding is inside the background, margin is outside it. If you want more coloured space around text, that is padding. If you want space between two elements, that is margin.
Padding also enlarges the clickable area of a link or button, which margin does not — worth remembering for touch targets on mobile.
Why width Does Not Mean Width
This is the part that confuses everyone at first.
.box {
width: 300px;
padding: 20px;
border: 5px solid black;
}How wide is that element on screen? Not 300px.
content 300px
+ padding 40px (20 left + 20 right)
+ border 10px (5 left + 5 right)
──────────────────
total 350pxBy default, width sets the content box only. Everything else is added outside it.
The consequence in real layouts is worse than a wrong number. Give two columns width: 50% and any padding, and they no longer fit side by side — total width exceeds 100% and the second wraps below. Generations of developers worked around this with nested wrapper divs, one for the width and one for the padding.
Note that margin is never included in width under either box-sizing model. It is space around the element, not part of it.
border-box: The One-Line Fix
.box {
box-sizing: border-box;
width: 300px;
padding: 20px;
border: 5px solid black;
}
/* total width: exactly 300px — content shrinks to 250px to make room */With border-box, the width you set is the final rendered width including padding and border. The content area adjusts automatically.
This is what people intuitively expect, and it makes percentage widths usable — two columns at 50% with padding genuinely fit.
Apply it globally. Practically every CSS reset and framework does this, and it is the standard opening of a stylesheet:
*, *::before, *::after {
box-sizing: border-box;
}The pseudo-elements are included because they are boxes too and would otherwise keep the old behaviour.
A slightly more flexible version lets components opt out, which matters if you ever integrate a third-party widget that assumes content-box:
html { box-sizing: border-box; }
*, *::before, *::after { box-sizing: inherit; }There is no real downside. The default exists for historical compatibility, not because it is better.
Margin Collapsing
The other box model behaviour that produces confusing results.
Adjacent vertical margins collapse into one — the larger of the two, not the sum.
.a { margin-bottom: 30px; }
.b { margin-top: 20px; }
/* Gap between them: 30px, not 50px */This applies only to vertical margins. Horizontal margins never collapse and always add.
It also happens between a parent and its first or last child, which is the version that really confuses people:
<div class="parent">
<p>Text</p> <!-- p has a default top margin -->
</div>
/* The child's top margin escapes and pushes the PARENT down */You style the parent, expect space inside it, and instead the whole parent moves. The fixes: give the parent any padding or border, or make it a flex or grid container — margins do not collapse inside flex or grid containers at all.
That last point is a good argument for using gap in flex and grid layouts instead of margins. Gaps never collapse and never escape, so spacing behaves predictably.
Inspecting and Debugging Boxes
Devtools shows the box model directly. Inspect an element and look at the diagram in the Computed panel — it displays the exact content, padding, border and margin values, colour-coded, and hovering each region highlights it on the page.
That diagram answers most "why is there a gap here" questions in seconds, and it is faster than reading the CSS.
A temporary trick for seeing every box at once:
* { outline: 1px solid red; }Use outline rather than border — outline does not take up space, so it will not change the layout you are trying to diagnose. Adding a border to everything shifts the entire page and hides the problem.
Three habits worth adopting:
- Choose one direction for margins. Using only
margin-bottomfor vertical rhythm avoids collapsing surprises entirely, because there is never an adjacent pair. - Prefer
gapin flex and grid layouts over margins on children. - Use padding for internal space, margin for external. Padding is inside the background and enlarges click targets; margin separates elements.
Also worth knowing: width and height have no effect on inline elements such as span and a. Vertical padding on them overflows without pushing anything. Set display: inline-block or block when you need box dimensions.
