The box model
Every element is a rectangle made of four nested layers — and one setting changes how the maths works.
You write width: 200px on a card, add padding: 16px to give it some breathing room, and the card comes out 232 pixels wide — not 200. Then you set box-sizing: border-box and the same card comes out 200 pixels wide, exactly as you wrote. Both behaviours are correct CSS. The difference between them is the heart of the box model, and once you can see the four layers clearly, the maths stops being surprising.
The four layers
Every element on a page is, geometrically, a rectangle. That rectangle is built out of four nested boxes, working from the inside out:
- Content — where the text, image, or child elements actually sit. Its size is the
widthandheightyou set. - Padding — clear space inside the border, between the content and the border. Same background as the element.
- Border — the visible line drawn around the padding. Has a width, a style, and a colour.
- Margin — clear space outside the border, separating this element from its neighbours. Always transparent — it never has a background.
Picture a framed photo. The photo itself is the content. The matting around the photo is the padding — same colour scheme as the frame, sitting just inside the wood. The wood frame is the border. The empty wall space between this frame and the next painting is the margin.
Each layer has a size. padding-top, padding-right, padding-bottom, padding-left set the four padding sides individually; the shorthand padding lets you set them all at once. Borders work the same way (border-top-width and so on, plus the border shorthand for all of them). Margin follows the same pattern.
.card {
width: 200px;
padding: 16px;
border: 2px solid #c96442;
margin: 24px;
}That card has a 200-pixel content area, 16 pixels of padding inside the border on every side, a 2-pixel border around it, and 24 pixels of empty space outside the border. Five pieces of information, four nested boxes. The next question is: how much total horizontal space does this card take up?
Why width sometimes lies
By default, the width you set is the width of the content box — the innermost layer. Padding and border get added on top of that. So the card from the snippet above is actually:
content 200px
padding 16 + 16 = 32px
border 2 + 2 = 4px
total = 236px
You wrote width: 200px, but the box on the page is 236 pixels wide. Add the 24px margin on each side and the card eats 284px of horizontal real estate. This is the classic "I gave it 200 pixels and the layout broke" surprise.
This default is called box-sizing: content-box, and it has been around since CSS 1. The reason it exists is historical — back when CSS was new, "width" meaning "the content area" was the literal interpretation of the box model's geometry. In practice, almost nobody wants this behaviour. When you say "this card should be 200 pixels wide", you almost always mean the visible card, including its border, not the content area inside it.
.three-cards { display: flex; gap: 0; }
.card {
width: 33.333%;
padding: 16px;
border: 1px solid #ccc;
}Three cards, each one-third of the container — except each one is one-third plus 32px of padding plus 2px of border. The total exceeds 100% of the container, the third card wraps to a second row, and the layout collapses. Nothing is "wrong" with the CSS — it is doing exactly what content-box says — but the maths is fighting you.
box-sizing, an element has width: 300px; padding: 20px; border: 4px solid;. How wide is it on the page?box-sizing: border-box
The fix is one declaration. box-sizing: border-box changes what width means: it now covers the content plus the padding plus the border. Set width: 200px with border-box and the visible card is 200 pixels wide, no matter how much padding or border you add inside it. Padding eats into the content area instead of pushing the border outward.
*, *::before, *::after {
box-sizing: border-box;
}That one rule, often called "the asterisk rule," is in the first few lines of nearly every modern CSS reset. It applies border-box to every element on the page (including pseudo-elements created with ::before and ::after). Once it is set, width: 200px means the box on the screen is 200 pixels, every time, and the whole "padding adds to my width" pain disappears.
Apply box-sizing: border-box universally at the top of your stylesheet and never think about it again. You give up nothing — the rare case where you actually want padding to add on top of width is a one-line opt-out (box-sizing: content-box) on that one element.
* { box-sizing: border-box }, then write .card { width: 200px; padding: 24px }. How much of that 200px is left for the content?Margins are the strange one
Margins behave oddly in two specific ways, and recognizing both saves real debugging time.
First, margins can be negative. margin-top: -8px pulls the element 8 pixels upward, overlapping its previous sibling. This is occasionally useful (pulling a card up to overlap its container's edge, for example), and occasionally a clue you should be reaching for a different layout tool entirely.
Second, vertical margins collapse. When two block elements sit on top of each other, their adjacent margins do not add up — the bigger one wins. Two paragraphs with margin-bottom: 24px and margin-top: 24px give you 24 pixels of space between them, not 48. The browser collapses the two margins into one.
p { margin-top: 24px; margin-bottom: 24px; }In a stack of paragraphs, you might expect 48 pixels between each pair. You actually get 24. This is intentional — it is what makes long documents look right with default margins — but it confuses people who expect maths to be additive. Margin collapse only happens vertically (block direction), and only between adjacent block elements. Padding never collapses, and horizontal margins never collapse.
The rule of thumb: when you find yourself fighting margin collapse, switching to gap on the parent (with flex or grid) is almost always the cleaner answer. Gap does not collapse, applies once, and lives on the parent where the spacing decision belongs.
Margin collapse stops the moment a parent has padding or border on the side where the margins meet. That is why <section style="padding: 1px"> sometimes "fixes" a margin gap that confused you — it broke the collapse, not the margin itself.
<p style="margin-bottom: 32px"> followed by <p style="margin-top: 16px">. How much vertical space sits between them?width: 33.333%; padding: 16px; border: 1px solid; — and the third card wraps onto a new row. What's the most likely fix?