Every Element Is a Box With Four Layers
Set an element to 300 pixels wide, add padding, and measure it: it is 340. That one behaviour has confused every person who has ever learned CSS, and one line of code fixes it permanently.
Worth reading first: Three Ways In, One You Should Use
Set an element to 300 pixels wide, add padding, and measure it: it is 340. That one behaviour has confused every person who has ever learned CSS, and one line of code fixes it permanently.
Content, padding, border, margin — from the inside out
Every element the browser draws is a rectangle, and every rectangle has four nested layers. Not some elements. Every one.
The text or image itself. Its size is what width and height normally describe.
Space INSIDE the border. It takes the background colour, so it is what gives a button room around its label.
A line around the padding. Has a width, a style, and a colour, and all three are needed for it to appear.
Space OUTSIDE the border. Always transparent — it shows whatever is behind the element, never its own background.
margin 16
padding 20
content
300px
width: 300px; padding: 20px; border: 4px solid; box-sizing: content-box;
300 + 20×2 + 4×2 = 348px on screen
+ 16×2 margin = 380px of horizontal space consumed
You asked for 300px and the element occupies 348px. With content-box, width means the content only — padding and border are added on the outside. Two of these side by side in a 50%-wide container overflow, and this is why.
Margin is outside the box in both modes and is never included in width. That is the one part border-box does not change.
Drag the sliders and watch the sum at the bottom. That arithmetic is the whole chapter.
padding: 20px; /* all four sides */padding: 10px 20px; /* top+bottom | left+right */padding: 10px 20px 30px; /* top | left+right | bottom */padding: 10px 20px 30px 40px; /* top | right | bottom | left — clockwise */ margin: 0 auto; /* 0 top and bottom, auto left and right */border: 2px solid #3e7f5c; /* width | style | colour */margin: 0 auto is worth memorising: it centres a block element horizontally by splitting the leftover space equally. It only works on an element that has a width — an element already filling its container has no leftover space to split.
width means the content box, by default
Here is the behaviour that surprises everybody. By default, width sets the width of the content only. Padding and border are added on the outside.
.card { width: 300px; padding: 20px; border: 4px solid;} Actual space on screen: 300 content+ 40 padding (20 left + 20 right)+ 8 border (4 left + 4 right)──────── 348pxYou asked for 300 and got 348. Nothing warns you, and the CSS clearly says width: 300px.
border-box makes width mean what you meant
box-sizing changes what width refers to. Set it to border-box and width means the whole visible box — padding and border included, taken out of the width rather than added to it.
*, *::before, *::after { box-sizing: border-box;}content-box — the default
width: 300px + 20px padding + 4px border = 348px on screen. The number you type is the content, and the final size depends on three properties at once.
border-box — what you want
width: 300px + 20px padding + 4px border = 300px on screen, with 252px of content. Change the padding and the box stays 300. Two 50% columns fit.
There is no downside and essentially every real project starts with it. It is not the default only because changing a default would have broken a large part of the existing web — the specification could not fix it retroactively, so you fix it in two lines.
Vertical margins collapse into each other
The second surprising behaviour. When two vertical margins meet, they do not add up — the larger one wins and the smaller is discarded.
h2 { margin-bottom: 30px; }p { margin-top: 20px; } Gap between them: 30px, not 50px.This is deliberate and mostly helpful — it is why a document of headings and paragraphs spaces evenly without you calculating anything. It becomes confusing in two other cases.
Two elements next to each other. The larger of the two margins is the gap. The common case, and the helpful one.
A child's top margin can escape its parent and push the PARENT down instead — leaving a gap above the box you did not ask for.
An element with no content, padding, or border has its own top and bottom margins collapse together into one.
Left and right margins always add. Only vertical margins collapse.
The escaping-child case is the one that produces a genuine bug report. Anything that establishes a new formatting context stops it — padding, a border, or the modern one-liner:
.parent { padding-top: 1px; } /* works, and is a hack */.parent { border-top: 1px solid; } /* works, and is visible */.parent { display: flow-root; } /* the intended answer */ /* Flex and grid containers do not collapse margins at all, which is why this problem largely disappears in chapters 17–18. */Padding versus margin is inside versus outside
Both create space and they are not interchangeable. The test is whether the space belongs to the element or sits between elements.
Padding — inside
Takes the background colour. Included in the clickable area. Does not collapse. Room around a button's label, breathing space inside a card, the inset on a form field.
Margin — outside
Always transparent. Not clickable. Collapses vertically. The gap between two cards, the space under a heading, centring with 0 auto.
The clearest case is a link styled as a button. Padding enlarges the target — the whole padded area is clickable. Margin does not; it pushes neighbours away and stays dead space. On a phone that is the difference between an easy tap and a missed one.
Two more properties that stop boxes from misbehaving:
.container { max-width: 65ch; /* never wider than this — the readable measure */ margin: 0 auto; /* and centred */} img { max-width: 100%; height: auto; } /* never overflow */ .panel { min-height: 200px; } /* at least this tall, more if needed */max-width is almost always better than width for a container: it takes the full space when there is less and stops growing when there is more, which is responsive behaviour for free and no media query.
Key takeaways
- Every element is a rectangle with four layers: content, padding, border, margin.
- Padding takes the background colour; margin is always transparent.
- Shorthand values run clockwise from the top; two values mean vertical then horizontal.
- margin: 0 auto centres a block, but only one that has a width.
- By default width sets the CONTENT width, so padding and border are added on top — 300 becomes 348.
- That is why two 50% columns with padding do not fit on one line.
- box-sizing: border-box makes width mean the visible box. Put it on * at the top of every stylesheet.
- Vertical margins collapse to the larger of the two; horizontal margins always add.
- A child's top margin can escape its parent. display: flow-root stops it, and flex/grid containers never collapse at all.
- Padding is inside and clickable; margin is outside and dead space. On a phone that decides whether a tap lands.
- Prefer gap in flex and grid, and max-width over width for containers.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.