ESC

Type to search the knowledge base.

CSS Box Model

Content, padding, border, margin, box-sizing, and how the math actually works for layout and overflow.

beginner5 min read
  • css
  • box-model
  • layout
  • box-sizing

Every element is a box. Layout, overflow, click targets, and “why is this 20px wider than I said?” all come back to the box model: content + padding + border + margin, plus which of those count toward width / height.

Docs: MDN CSS box model, MDN box-sizing, web.dev Learn CSS — The box model.

The four layers

From inside out:

Layer Role
Content Text, images, or the area sized by width / height
Padding Space inside the border, still part of the element’s background
Border Edge drawn around padding + content
Margin Space outside the border; collapses with sibling margins in normal flow
.card {
  width: 300px;
  padding: 16px;
  border: 2px solid #333;
  margin: 24px;
}

With the default box-sizing: content-box, width: 300px means content only. The used outer width is roughly:

300 + 16×2 + 2×2 = 336px (before margin). Margin is outside the border box and does not add to the element’s own border-box size, but it does consume space in the layout.

content-box vs border-box

/* Default in CSS: content-box */
.box-a {
  box-sizing: content-box;
  width: 200px;
  padding: 20px;
  border: 5px solid;
  /* border-box width ≈ 200 + 40 + 10 = 250px */
}

/* Almost always what you want in UI work */
.box-b {
  box-sizing: border-box;
  width: 200px;
  padding: 20px;
  border: 5px solid;
  /* border-box width = 200px; content shrinks to fit */
}

border-box makes width / height include content + padding + border. Percent widths and grid/flex tracks stop “growing” when you add padding. Most design systems set this globally:

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

Some teams put it on html { box-sizing: border-box; } and inherit with *, *::before, *::after { box-sizing: inherit; } so third-party widgets can opt out. Either approach is fine; the point is predictable math.

Which box are you talking about?

When APIs and DevTools say “width,” they may mean different boxes:

Term Includes
Content box Just content
Padding box Content + padding
Border box Content + padding + border
Margin box Border box + margin
  • getBoundingClientRect() → border box (viewport coordinates)
  • offsetWidth / offsetHeight → border box (layout pixels, integers)
  • clientWidth / clientHeight → padding box minus scrollbars
  • scrollWidth / scrollHeight → full scrollable content size

Reading these after style changes can force layout — see the rendering pipeline. Don’t thrash offsetHeight in a loop.

Margin collapse (normal flow)

Vertical margins of block-level elements in normal flow can collapse into one margin: the larger of the two wins (with rules for negative margins). Classic surprise:

.section {
  margin-block: 2rem; /* 32px top and bottom… */
}
.section + .section {
  /* …but adjacent siblings collapse: gap is 32px, not 64px */
}

Collapse does not apply the same way for flex/grid items, floated elements, or absolutely positioned boxes. If two sections suddenly have “half the gap you expected,” check collapse before blaming the cascade.

Parent/first-child collapse is another trap: a child’s top margin can stick out of a parent with no padding/border/overflow other than visible. Fix with padding, border, or display: flow-root (new BFC) on the parent.

Padding, borders, and hit areas

  • Padding increases the clickable area of buttons and links — good for touch targets.
  • Borders sit inside the border box under border-box, so a 1px border change can reflow neighbors if you didn’t reserve space (outline or transparent border placeholders avoid layout jump).
  • outline does not take space in the box model — preferred for focus rings when you want no layout shift (outline vs border).

Inline vs block boxes

Inline-level boxes (span, a without display: block) still have padding and borders, but vertical margins don’t create the same block spacing, and width / height often don’t apply the way they do on blocks. For “chip” UI, use inline-flex or inline-block so the box model matches what designers expect.

.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 999px;
}

Overflow and the content box

overflow clips or scrolls the padding edge (overflow is defined relative to the padding box). Common layout bug in flex/grid: a child with long text won’t shrink below its min-content size, so the parent overflows even with overflow: auto. Pair with min-width: 0 on flex/grid children — covered in Flexbox fundamentals.

.panel {
  display: flex;
  min-height: 0;
}
.panel__body {
  flex: 1;
  min-width: 0;
  overflow: auto;
}

Percentage padding and margin

Percentage padding and margin on physical properties are resolved against the width of the containing block (including vertical padding in classic CSS). That odd rule is why “padding-top: 56.25%” used to fake 16:9 ratios before aspect-ratio. Prefer aspect-ratio for media slots — fewer box-model hacks, better for CLS.

Debugging checklist

  1. In DevTools, inspect box model diagram (content / padding / border / margin).
  2. Confirm box-sizing on that element (inherited globals can surprise you).
  3. Is the surprise width from padding/border under content-box?
  4. Is vertical space “missing” because of margin collapse?
  5. Is overflow from min-content size, not from “wrong width”?
  6. Did you measure the wrong box (clientWidth vs offsetWidth)?

Interview angle

Define content / padding / border / margin. Explain content-box vs border-box with one numeric example. Mention margin collapse for adjacent blocks and that flex/grid items don’t collapse the same way. Bonus: which box getBoundingClientRect reports.

Live coding: build a card at exactly 320px outer border-box with 24px padding and a 2px border — without calculator thrash — using border-box.

Further reading

Related guides