Lesson 14 of 32
The Box Model
Content, padding, border and margin as nested boxes, the real difference between content-box and border-box sizing, and how vertical margins collapse between block elements.
Every single element on a page, no matter what it is, renders as a rectangular box. Understanding what that box is actually made of — and where its edges really are — is the difference between CSS that behaves the way you expect and CSS that seems to randomly overflow, misalign, or leave gaps you didn't ask for.
Four nested boxes
Every element's box is built from four layers, nested inside each other from the inside out:
- Content — the actual text or child elements, sized by
widthandheight. - Padding — space between the content and the border, filled with the element's own background.
- Border — a visible (or invisible) line wrapping the padding.
- Margin — space outside the border, separating this element from its neighbours. Margin is always transparent — it's not part of the element's own visual box at all, just spacing around it.
.card {
width: 200px;
padding: 20px;
border: 5px solid #333;
margin: 10px;
}Reading from the inside out: a 200px content area, wrapped in 20px of padding on every side, wrapped in a 5px border, with 10px of margin pushing neighbouring elements away. Padding and border both add to the space the box actually occupies on the page; margin never overlaps with the box's own background — it's pure separation from what's around it.
box-sizing: what width actually measures
Here's the part that trips almost everyone up at least once. By default,
width and height apply to the content box only — padding and border
get added on top, making the element's true rendered size bigger than the
width you wrote.
.content-box {
box-sizing: content-box; /* the default, even if you never write this line */
width: 200px;
padding: 20px;
border: 5px solid;
}
/* rendered width = 200 (content) + 40 (padding, both sides) + 10 (border, both sides) = 250px */Set box-sizing: border-box instead, and width/height become the
element's total rendered size, with padding and border eating into that
budget instead of adding to it:
.border-box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 5px solid;
}
/* rendered width = 200px, full stop — content area shrinks to 160px to fit inside it */border-box is why the real, standard professional pattern is to reset it
globally, once, at the top of a stylesheet:
*, *::before, *::after {
box-sizing: border-box;
}The reason developers reach for this almost universally: without it, giving
an element width: 50%; padding: 1rem; makes its true rendered width
more than 50% of its container, because the padding adds on top of that
percentage — and that's rarely what anyone actually wants. With the reset in
place, width: 50% really does mean 50%, padding and all, which makes
laying out a page with percentages and fixed padding together predictable
instead of a constant source of overflow.
Margin collapsing
Vertical margins between adjacent block-level siblings don't add together — they collapse to the larger of the two. This is real, spec-defined CSS behaviour, not a bug, and it only applies to vertical margins in normal document flow (never horizontal margins, and never inside a flex or grid container, where this rule doesn't apply at all).
.first { margin-bottom: 30px; }
.second { margin-top: 20px; }<div class="first">First box</div>
<div class="second">Second box</div>You might expect 50px between the two boxes (30 + 20). The real result is 30px — the larger of the two margins, not the sum. The browser treats the two touching margins as one shared gap rather than two separate pushes.
This only happens between elements that are genuinely adjacent in normal
flow with nothing between them — no border, no padding, no content, and no
display: flex or display: grid on their shared parent. A container with
even 1px of padding between the two elements breaks the collapse, because
the margins no longer actually touch.
Checking this in the browser
You don't have to work any of this out by eye. Every modern browser's devtools has a visual box model inspector — select an element and it draws the content/padding/border/margin as labelled, colour-coded rectangles with the exact pixel values for each side. A later lesson covers devtools in full; for now, just know it exists and it's the fastest way to confirm what box model math is actually happening on a real element.
Try it yourself
Two boxes side by side compare content-box and border-box at the exact
same declared width, padding and border — watch how differently wide
they actually render. Below that, two stacked boxes demonstrate margin
collapsing directly: the gap between them is 40px, the larger of the two
margins, not 70px.
What to remember
- Every box is content, padding, border, and margin, nested from the inside out — padding and border add to an element's rendered size, margin only pushes neighbours away.
content-box(the default) sizeswidth/heightto content only;border-boxsizes them to the total including padding and border.*, *::before, *::after { box-sizing: border-box; }is close to a universal default in real projects, because it keeps percentage widths plus padding predictable.- Adjacent vertical margins between block-level siblings collapse to the larger value, not their sum — and only in normal flow, never in flex or grid.
- Devtools has a visual box model inspector for checking all of this on a real element — more on that in a later lesson.
Check yourself
4 questions · pass 3/4 to unlock Display and Visibility
1.An element has
width: 200px; padding: 20px; border: 5px solid;and the defaultbox-sizing. How wide is its total rendered box?2.With
box-sizing: border-box, an element haswidth: 200px; padding: 20px; border: 5px solid;. How wide is its total rendered box?3.Two stacked block elements have
margin-bottom: 30pxon the first andmargin-top: 20pxon the second. How much vertical space ends up between them?4.Why do most developers apply
*, *::before, *::after { box-sizing: border-box; }as a global reset?
4 left to answer