AniUI Academy

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.

9 min read

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:

  1. Content — the actual text or child elements, sized by width and height.
  2. Padding — space between the content and the border, filled with the element's own background.
  3. Border — a visible (or invisible) line wrapping the padding.
  4. 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.

Try it yourself
Loading playground...

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) sizes width/height to content only; border-box sizes 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

up to 50
  1. 1.An element has width: 200px; padding: 20px; border: 5px solid; and the default box-sizing. How wide is its total rendered box?

  2. 2.With box-sizing: border-box, an element has width: 200px; padding: 20px; border: 5px solid;. How wide is its total rendered box?

  3. 3.Two stacked block elements have margin-bottom: 30px on the first and margin-top: 20px on the second. How much vertical space ends up between them?

  4. 4.Why do most developers apply *, *::before, *::after { box-sizing: border-box; } as a global reset?

4 left to answer