The box model, and why box-sizing: border-box exists
Every element the browser paints is a rectangular box, and that box has four
concentric parts: the content in the middle, the padding around it, the
border around that, and the margin outside the border. Layout bugs that
look mysterious — a two-column grid that overflows its container, a card that is
suddenly wider than its siblings — are almost always a disagreement about which
of those parts the word width refers to. There is a single property that
settles it, and knowing what it does is most of what “understanding the box
model” means in practice.
width counts the content alone (content-box) or the whole painted box (border-box).The default counts the wrong thing
CSS ships with box-sizing: content-box. Under that rule, width sets the size
of the content box only — padding and border are then added on top. So this
card, which you asked to be half its container, is not half its container:
.col {
width: 50%; /* content is 50% ... */
padding: 1rem; /* ... plus 16px each side ... */
border: 1px solid; /* ... plus 1px each side */
}
/* two .col side by side = 100% + 4rem + 4px → they overflow and wrap */
Two of these will not fit in one row, because each is 50% plus 34px. The
numbers you wrote and the numbers the browser lays out are different numbers,
and nothing in the CSS tells you that unless you already know the default.
border-box makes width mean what you meant
Switch the measurement and width becomes the size of the whole painted box
— padding and border are absorbed inward instead of pushed outward. Now 50%
is genuinely 50% of the container, whatever padding you add later:
.col {
box-sizing: border-box;
width: 50%;
padding: 1rem; /* eats into the 50%, doesn't add to it */
border: 1px solid;
}
/* two .col side by side = exactly 100% → they fit */
This is why nearly every stylesheet opens with a reset that flips the default
for everything at once. Apply it to * and to the pseudo-elements, and inherit
it so component authors can opt a subtree back out if they ever need to:
*, *::before, *::after {
box-sizing: border-box;
}
Margin is outside the box, and it collapses
One part sits apart from the rest: margin is space between boxes, not part of
the box, so no box-sizing value touches it — a margin always adds to the
footprint. And vertical margins between block siblings collapse: a 24px
bottom margin meeting a 16px top margin produces 24px of gap, not 40px.
That surprises people who expect the two to sum, and it is why a gap looks
smaller than the arithmetic. When you need a gap that never collapses and never
surprises, reach for gap on a flex or grid container instead — it is defined
between items and does not participate in collapsing at all.
The whole model reduces to one habit: set border-box globally, then read
width and height as the size of the visible box, and treat margin as the
space around it. Once measurement is predictable, the overflow bugs stop, and
sizing a responsive layout becomes arithmetic you can trust. The responsive
image exercise is a good place to feel a box whose dimensions have to stay
honest as the viewport changes.