Skip to the content.

The box model, and why box-sizing: border-box exists

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.

The four boxes, and what each box-sizing value counts as width Concentric boxes for margin, border, padding and content. With content-box, width measures the content only; with border-box, width measures content plus padding plus border. margin border padding content content-box width = content only padding + border ADD to it border-box width = content + padding + border padding + border eat INTO it
The same four boxes; the only question is whether 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.