Quick Answer

Every element is a box with four layers: content, then padding, then border, then margin. By default the width property sets only the content width, so padding and border are added on top and the element ends up wider than the number you wrote. Setting box-sizing: border-box makes width include padding and border, which is what almost everyone expects, and applying it globally is standard practice.

The Four Layers

From the inside out:

┌─────────────────────────────────────┐
│           margin (outside)          │
│  ┌───────────────────────────────┐  │
│  │          border               │  │
│  │  ┌─────────────────────────┐  │  │
│  │  │        padding          │  │  │
│  │  │  ┌───────────────────┐  │  │  │
│  │  │  │     content       │  │  │  │
│  │  │  └───────────────────┘  │  │  │
│  │  └─────────────────────────┘  │  │
│  └───────────────────────────────┘  │
└─────────────────────────────────────┘

Content — the text or child elements.
Padding — space inside the border. Takes the element's background.
Border — the visible edge.
Margin — space outside the border, always transparent.

The practical distinction between padding and margin: padding is inside the background, margin is outside it. If you want more coloured space around text, that is padding. If you want space between two elements, that is margin.

Padding also enlarges the clickable area of a link or button, which margin does not — worth remembering for touch targets on mobile.

Why width Does Not Mean Width

This is the part that confuses everyone at first.

.box {
  width: 300px;
  padding: 20px;
  border: 5px solid black;
}

How wide is that element on screen? Not 300px.

content       300px
+ padding      40px   (20 left + 20 right)
+ border       10px   (5 left + 5 right)
──────────────────
total         350px

By default, width sets the content box only. Everything else is added outside it.

The consequence in real layouts is worse than a wrong number. Give two columns width: 50% and any padding, and they no longer fit side by side — total width exceeds 100% and the second wraps below. Generations of developers worked around this with nested wrapper divs, one for the width and one for the padding.

Note that margin is never included in width under either box-sizing model. It is space around the element, not part of it.

border-box: The One-Line Fix

.box {
  box-sizing: border-box;
  width: 300px;
  padding: 20px;
  border: 5px solid black;
}

/* total width: exactly 300px — content shrinks to 250px to make room */

With border-box, the width you set is the final rendered width including padding and border. The content area adjusts automatically.

This is what people intuitively expect, and it makes percentage widths usable — two columns at 50% with padding genuinely fit.

Apply it globally. Practically every CSS reset and framework does this, and it is the standard opening of a stylesheet:

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

The pseudo-elements are included because they are boxes too and would otherwise keep the old behaviour.

A slightly more flexible version lets components opt out, which matters if you ever integrate a third-party widget that assumes content-box:

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

There is no real downside. The default exists for historical compatibility, not because it is better.

Margin Collapsing

The other box model behaviour that produces confusing results.

Adjacent vertical margins collapse into one — the larger of the two, not the sum.

.a { margin-bottom: 30px; }
.b { margin-top: 20px; }

/* Gap between them: 30px, not 50px */

This applies only to vertical margins. Horizontal margins never collapse and always add.

It also happens between a parent and its first or last child, which is the version that really confuses people:

<div class="parent">
  <p>Text</p>      <!-- p has a default top margin -->
</div>

/* The child's top margin escapes and pushes the PARENT down */

You style the parent, expect space inside it, and instead the whole parent moves. The fixes: give the parent any padding or border, or make it a flex or grid container — margins do not collapse inside flex or grid containers at all.

That last point is a good argument for using gap in flex and grid layouts instead of margins. Gaps never collapse and never escape, so spacing behaves predictably.

Inspecting and Debugging Boxes

Devtools shows the box model directly. Inspect an element and look at the diagram in the Computed panel — it displays the exact content, padding, border and margin values, colour-coded, and hovering each region highlights it on the page.

That diagram answers most "why is there a gap here" questions in seconds, and it is faster than reading the CSS.

A temporary trick for seeing every box at once:

* { outline: 1px solid red; }

Use outline rather than border — outline does not take up space, so it will not change the layout you are trying to diagnose. Adding a border to everything shifts the entire page and hides the problem.

Three habits worth adopting:

  • Choose one direction for margins. Using only margin-bottom for vertical rhythm avoids collapsing surprises entirely, because there is never an adjacent pair.
  • Prefer gap in flex and grid layouts over margins on children.
  • Use padding for internal space, margin for external. Padding is inside the background and enlarges click targets; margin separates elements.

Also worth knowing: width and height have no effect on inline elements such as span and a. Vertical padding on them overflows without pushing anything. Set display: inline-block or block when you need box dimensions.

Frequently Asked Questions

What is the difference between padding and margin? Padding is space inside the border and takes the element's background, so it enlarges the visible box and the clickable area. Margin is space outside the border, always transparent, and separates the element from its neighbours.
Should I always use border-box? Yes, in practically all cases. Apply it globally with the universal selector including pseudo-elements. Every CSS framework does this, and it makes percentage widths with padding behave the way people expect.
Why is my element wider than the width I set? Because the default content-box sizing adds padding and border outside the width. A 300px box with 20px padding and a 5px border renders at 350px. Setting box-sizing: border-box makes width the final rendered width.
What is margin collapsing? Adjacent vertical margins merge into the larger of the two rather than adding. It also happens between a parent and its first or last child, where the child's margin can push the parent down. Horizontal margins never collapse.
How do I stop margins collapsing? Add padding or a border to the parent, or make it a flex or grid container — margins never collapse inside those. Using gap for spacing instead of margins on children avoids the problem entirely.