← back to the section

Every element on a page is a rectangular box, even if it looks like a circle. What this box is made of and how boxes push each other apart is the box model, the most fundamental layer of CSS. It is also home to two historical surprises — the "wrong" width and collapsing margins — that everyone runs into.

The four layers of the box

From the inside out: content (the content itself) → padding (the field between content and border) → border (the frame) → margin (the outer gap to neighbors).

.card {
  width: 300px;
  padding: 16px;
  border: 2px solid #ddd;
  margin: 24px;
}

The practical difference between padding and margin: padding is inside the element — the element is clickable on it and the background shows there; margin is outside — transparent and "not owned" by the element. Enlarging a button's clickable area — padding; pushing the button away from its neighbor — margin.

box-sizing: why width "lies"

Historical surprise number one: by default width sets the width of the content only. The card above will occupy 300 + 16·2 + 2·2 = 336px. You added padding — the element bloated, the layout broke.

The box-sizing: border-box mode changes the meaning of width to the intuitive one: "the whole box including padding and border = 300px", and the content shrinks by itself. The first lines of almost any project:

*, *::before, *::after {
  box-sizing: border-box;
}

This is so much the standard that when you encounter "mysteriously" bloating elements in an old project, the first thing to check is whether this reset is present.

More about sizes: width/height are rigid; min-width/max-width are constraints on top. The fluid text column idiom is max-width: 65ch; width: 100%: it stretches, but never wider than a comfortable line length. And memorize the percentage pairs: width: 50% is relative to the parent's width, but padding: 10% and even margin-top: 10% are computed also from the width of the parent (not the height!) — a historical fact the "fixed aspect ratio block" trick is built on, but one that surprises in vertical spacing. For proportions today there is the honest property aspect-ratio: 16 / 9.

Margin collapse: where did the margin go

Surprise number two. Vertical margins of adjacent blocks do not add up — they collapse: the larger one wins.

h2 { margin-bottom: 24px; }
p  { margin-top: 16px; }
/* the distance between them is 24px, not 40px */

For adjacent paragraphs this is even convenient (gaps don't double up). Two other cases are inconvenient:

  • Parent and first child. The first child's margin-top "leaks" outward and moves the parent: the card's header drifts away even though the card itself has no margin. Collapsing through the parent-child boundary is stopped by padding or border on the parent, or by the modern display: flow-root.
  • Empty elements — their top and bottom margins collapse with each other.

It's important to know the boundaries of the phenomenon: only vertical margins of block-level elements in normal flow collapse. In flex and grid containers there is no collapsing at all — one more reason life is easier with them. There, spacing between children is also better set with the gap property rather than margin.

Negative margins are legal: margin-top: -8px pulls the element up, negative side margins stretch the element wider than its parent (the classic "full-width strip inside a narrow column" trick). It's a sharp tool — use it deliberately.

border-radius and box-shadow: the geography of the box

border-radius rounds the corners — from a subtle 4px to a circle at 50% (for a square element). Values can be set per corner, and via a slash — elliptically (border-radius: 60px / 30px). Rounding clips the background and the border, but not the content: an image inside a rounded card sticks out at the corners until the card gets overflow: hidden.

box-shadow draws a shadow along the box's outline (already accounting for the radius): x y blur spread color, the inset keyword — a shadow inward, and comma-separated — multiple layers:

.card {
  box-shadow:
    0 1px 2px rgba(0,0,0,.06),
    0 8px 24px rgba(0,0,0,.10); /* layered "soft" shadow */
}

The shadow takes no part in layout — it doesn't push neighbors and doesn't change the box's size. That makes it handy for hover states: the shadow appears, the layout doesn't flinch. The same property is often used to draw a "border without a shift": box-shadow: 0 0 0 2px blue instead of border, which would have changed the dimensions.

In short

  • The box: content → padding → border → margin. Padding is the clickable area with background, margin is the transparent distance to neighbors.
  • box-sizing: border-box on everything is the standard reset: width means the whole box, not just the content.
  • Percentages in padding/margin are computed from the parent's width; for proportions — aspect-ratio.
  • Vertical margins collapse: between siblings the larger one wins, a child's margin leaks through a parent without padding/border. In flex/grid there is no collapsing; spacing there goes through gap.
  • border-radius clips the background but not the content (needs overflow: hidden); box-shadow doesn't affect layout — perfect for hover states.
  • Document flow: display and the float legacy — how boxes line up one after another.
  • Units and sizing — px, rem, percentages, and clamp: what to measure boxes with.
  • Cascade and specificity — if you missed the start of the series.