The Box Model
By the end of this lesson
Predict how size, padding, border and margin combine.
Every element on a page is a rectangle, even when rounded corners hide it. Around the content sit three rings: padding, then border, then margin.
Once you can work out the real width of a box from those numbers, a whole category of "why is this overflowing?" disappears.
The four layers, from the inside out:
- Content
- The text or child elements. What width and height describe, by default.
- Padding
- Space inside the box, between the content and the border. It takes the background colour, so padding is what makes a card look roomy.
- Border
- A line drawn at the edge of the padding. It has a thickness, so it takes up space.
- Margin
- Space outside the box, pushing other elements away. It is transparent — the background does not extend into it.
.employee-card {
width: 240px;
padding: 16px;
border: 2px solid #d8dde3;
margin: 12px;
}- By default, width sets the content width only. So this card's content area is 240px.
- Padding adds 16px on each side: 240 + 32 = 272.
- The border adds 2px on each side: 272 + 4 = 276. That is the width the card actually occupies.
- Margin adds 12px of space each side, so the card needs 300px of room, but the visible box is still 276.
- Put three of these in a 768px container expecting 720px of cards, and you get 900px and an overflow. The maths was never wrong — the default meaning of width is not the one most people assume.
*,
*::before,
*::after {
box-sizing: border-box;
}
.employee-card {
width: 240px;
padding: 16px;
border: 2px solid #d8dde3;
}- box-sizing: border-box redefines width to mean the whole visible box, including padding and border.
- The card is now 240px wide in total. The content area shrinks to 204px to make room for the padding and border.
- Applying it to everything, including generated content, is the usual opening move in a stylesheet. It is easier to reason about, and it means adding padding never changes an element's footprint.
- Margin is still outside, and still added on top. border-box changes what width covers; it does not absorb margin.
Margin collapse surprises everyone once. Two vertical margins that meet do not add up: the larger one wins and the smaller disappears. It applies between stacked block elements, and between a parent and its first or last child when nothing separates them.
It is not a bug. It exists so that a document of paragraphs each carrying a bottom and top margin gets even spacing rather than doubled gaps. It is still confusing the first time you set a 24px margin, then a 16px margin, and measure 24px.
/* Adjacent siblings: the gap is 24px, not 40px */
.expense-row {
margin-bottom: 24px;
}
.expense-row + .expense-row {
margin-top: 16px;
}
/* Parent and child: the child's margin escapes the parent */
.expense-list {
background: #f6f8fa;
/* no padding, no border */
}
.expense-list > .expense-row:first-child {
margin-top: 32px; /* pushes the whole list down instead */
}- In the first case the two margins meet, so the larger value applies and the total gap is 24px.
- In the second, there is nothing between the parent's top edge and the child's top margin, so the margins collapse together and the parent moves down. The coloured background does not start 32px above the first row, as intended — it starts 32px lower down the page.
- Padding or a border on the parent stops the collapse, because there is now something in between.
- Collapse is vertical only, and it does not happen inside a flex or grid container. That points at the most reliable way to avoid the whole topic: space children with gap on a flex or grid container instead of margins on the children. gap never collapses, it is stated once rather than per child, and there is no trailing margin on the last item to remove later.
- Where margins are the right tool, using one direction only — bottom margins, say — removes most collapse surprises, because two margins then rarely meet.
Summary
- Every element is content wrapped in padding, then border, then margin
- By default width means the content area, so padding and border make the box bigger
- box-sizing: border-box makes width mean the whole visible box, which is what most people expect
- Adjoining vertical margins collapse to the larger value, including between a parent and its first child
- Spacing children with gap avoids collapse entirely and removes the trailing-margin problem
Practice
Attempt each one before opening the solution. Getting it wrong first is how the idea sticks.
Try it yourself
Predict, then measure
Write a card with width: 300px, padding: 20px, a 1px border and margin: 10px. Work out on paper how wide the visible card is, and how much horizontal room it needs, with the default box-sizing.
Then add box-sizing: border-box and answer both again. Check yourself in the browser's element inspector, which shows the four layers.
Show solution
Default: content 300, plus 40 padding, plus 2 border, gives a visible card of 342px, needing 362px including margins.
With border-box: the visible card is 300px and the content area is 258px. It needs 320px including margins.
The point is not the arithmetic, it is that width means different things in the two modes. Almost everyone expects the border-box meaning, which is why setting it globally makes layouts behave.
*,
*::before,
*::after {
box-sizing: border-box;
}
.employee-card {
width: 300px; /* total visible width */
padding: 20px; /* content area becomes 258px */
border: 1px solid #d8dde3;
margin: 10px; /* outside, still added on */
}Think about it
Think about it
An expense list has a light grey background. The first row has margin-top: 32px, but instead of space inside the list, the whole list has moved down the page. What is happening, and give two fixes.
Show solution
The child's top margin has collapsed with the parent's, because nothing sits between the two top edges. The margin ends up outside the grey area rather than inside it.
Fix one: put the space on the parent as padding-top. That is what you wanted — space inside the box — and padding never collapses.
Fix two: remove the child margins and use gap on the list, with padding for the outer edge. That also handles the space between rows without a trailing margin on the last one.
Knowledge check
Nothing is recorded and there is no score. The explanation appears either way.
Saved in this browser only.