The box model

Predict the size of a box, and know why two margins sometimes become one.

04of 26
≈ 11Min read
8Parts

Four layers

Every element is a rectangle made of four nested areas: the content, then padding around it, then the border, then the margin outside that.

  • Padding is inside the border, so the background reaches into it.
  • Border is drawn between padding and margin.
  • Margin is outside everything and is always transparent — a background never shows there.
from the inside out
┌─────────────── margin ────────────────┐
│  ┌──────────── border ─────────────┐  │
│  │  ┌───────── padding ─────────┐  │  │
│  │  │                           │  │  │
│  │  │         content           │  │  │
│  │  │                           │  │  │
│  │  └───────────────────────────┘  │  │
│  └─────────────────────────────────┘  │
└───────────────────────────────────────┘

box-sizing decides what width means

By default, width sets the content width and padding and border are added on top. Set width: 300px; padding: 20px; border: 1px and the element occupies 342 pixels. That is content-box, and it is the reason the first line of almost every stylesheet is a reset.

write this once and forget it
*, *::before, *::after {
  box-sizing: border-box;
}
Valuewidth: 300px; padding: 20px; border: 1px
content-box (default)Content 300, total 342.
border-boxTotal 300, content 258.
Why border-box

It matches how people think: "this box is 300 wide". It also makes percentage widths work with padding, which content-box makes painful.

The shorthands

one to four values, always clockwise
padding: 16px;                 /* all four */
padding: 16px 24px;            /* block (top/bottom), inline (left/right) */
padding: 16px 24px 8px;        /* top, inline, bottom */
padding: 16px 24px 8px 12px;   /* top, right, bottom, left */

/* logical versions, which flip in right-to-left layouts */
padding-block: 16px;
padding-inline: 24px;
padding-block-start: 16px;

Margins collapse

Two vertical margins that meet do not add up — the larger one wins and the smaller disappears. A 30px margin-bottom meeting a 20px margin-top produces a 30px gap, not 50.

This is deliberate: it is what keeps paragraphs evenly spaced whatever their individual margins say. It happens only vertically, and never in flex or grid containers.

three ways it catches you
/* 1. between siblings — 30px, not 50 */
h2 { margin-bottom: 30px; }
p  { margin-top: 20px; }

/* 2. a parent and its first child: the child's margin escapes the parent */
.card { margin-top: 0; }
.card h3 { margin-top: 24px; }   /* pushes .card down, not h3 */

/* 3. an empty element's own top and bottom margins collapse together */
the clean fix
.card {
  display: flow-root;   /* exists purely to contain margins and floats */
}

Anything that creates a block formatting context stops collapsing at that boundary: overflow: hidden or auto, display: flow-root, display: flex or grid, position: absolute, a float, or any padding or border on the parent.

Centring with margin

margin: auto centres horizontally in normal flow, and in both directions inside a flex or grid container. It never centres vertically in normal flow.

the classic
.wrap {
  max-width: 1100px;
  margin-inline: auto;   /* equal left and right, so it centres */
}

min, max and the sizing keywords

max-width beats width, and min-width beats both. That order is fixed, and it is why width: 100%; max-width: 640px is the standard responsive pattern.

ranges, not fixed numbers
.panel {
  width: 100%;
  max-width: 640px;     /* never wider */
  min-height: 12rem;    /* never shorter */
}

.label {
  width: min-content;   /* as narrow as the longest word */
}

.button {
  width: max-content;   /* as wide as the content on one line */
}

.column {
  width: fit-content;   /* max-content, capped by the available space */
}

Borders and outlines are different

  • A border takes up space and moves everything around it. An outline is drawn on top and takes no space at all.
  • That makes outline the right tool for a focus ring: it never causes layout to shift.
  • outline-offset pushes the ring away from the element, which is often what makes it readable.
a focus ring that costs nothing
:focus-visible {
  outline: 2px solid #4c8dff;
  outline-offset: 3px;
}

What overflows

Content larger than its box spills out by default — it is visible, it just escapes. overflow: hidden clips it, auto gives a scrollbar when needed, clip clips with no scrolling at all.

A common cause of a page that scrolls sideways is one box slightly wider than the viewport. Find it with document.querySelectorAll("*") and a check on scrollWidth, or by setting outline: 1px solid red on everything.

Practice