The Box Model
Every Element on the Page Is a Rectangle
Before flexbox, grid, or any layout system — there's the box model. Every single element the browser renders is a rectangular box with four layers: content, padding, border, and margin. And here's what trips people up: the size you think you're setting and the size the browser actually renders are often different. That gap? That's the box model.
Margin collapse alone causes more confusion than any other CSS behavior. But once you understand the exact rules, you stop fighting it and start using it intentionally.
Think of the box model as a picture in a frame. The content is the picture itself. Padding is the matting inside the frame. Border is the frame itself. Margin is the wall space between this frame and the next. Here's the twist: when two frames hang side by side, their wall space overlaps — that's margin collapse. But add a glass pane (border or padding) between them, and each frame keeps its own space.
content-box vs border-box
The box-sizing property changes what width and height control:
content-box (the default)
.box {
box-sizing: content-box; /* Default */
width: 200px;
padding: 20px;
border: 2px solid black;
}
/* Total rendered width: 200 + 20 + 20 + 2 + 2 = 244px */
With content-box, width: 200px sets only the content area. Padding and border are added on top.
border-box (what you almost always want)
.box {
box-sizing: border-box;
width: 200px;
padding: 20px;
border: 2px solid black;
}
/* Total rendered width: 200px (padding and border included) */
/* Content area: 200 - 20 - 20 - 2 - 2 = 156px */
With border-box, width: 200px includes content + padding + border. The content area shrinks to accommodate.
The universal reset that every modern project uses:
*, *::before, *::after {
box-sizing: border-box;
}
Margin is never included in either box-sizing mode. width and height never account for margin. If you set width: 100% on an element with margin: 20px, it will overflow its container by 40px. Use width: auto (the default for block elements) or calc(100% - 40px) instead.
Margin Collapse: The Complete Rules
This is the part that burns people. Vertical margins between adjacent block-level elements collapse -- meaning they overlap rather than stack. It's easily the most misunderstood behavior in CSS.
Rule 1: Adjacent Siblings Collapse
.first { margin-bottom: 30px; }
.second { margin-top: 20px; }
/* Space between them: 30px (not 50px) */
/* The larger margin wins */
Rule 2: Parent and First/Last Child Collapse
If there's nothing separating a parent's margin from its child's margin, they collapse:
.parent { margin-top: 40px; }
.child { margin-top: 20px; }
/* Result: 40px margin above the parent (not 40px + 20px) */
/* The child's margin "escapes" through the parent */
Rule 3: Empty Elements Collapse Their Own Margins
.empty {
margin-top: 30px;
margin-bottom: 20px;
/* No content, padding, border, or height */
}
/* Takes up only 30px of space (larger margin wins) */
Rule 4: Negative Margins
When one margin is negative:
- Both positive: largest wins
- One negative, one positive: they are added (30px + -10px = 20px)
- Both negative: the most negative wins (-30px beats -10px)
.first { margin-bottom: 30px; }
.second { margin-top: -10px; }
/* Space between them: 20px (30 + -10) */
When Margins Do NOT Collapse
Honestly, this list is more important than the collapse rules themselves:
/* 1. Flex or grid items — their margins never collapse */
.flex-container { display: flex; flex-direction: column; }
.flex-container > * { margin: 20px 0; }
/* Full 40px between items, not 20px */
/* 2. Elements with overflow other than visible */
.parent { overflow: hidden; } /* or auto, scroll */
/* Child margins can't escape through parent */
/* 3. Elements with border or padding separating them */
.parent { padding-top: 1px; }
/* That 1px padding blocks parent-child collapse */
/* 4. Floated elements */
/* 5. Absolutely or fixed positioned elements */
/* 6. Inline-block elements */
/* 7. Elements that establish a BFC (Block Formatting Context) */
Production Scenario: The Disappearing Spacing
You will hit this bug. Everyone does. You add margin-top to the first child and the margin appears above the parent instead of inside it:
<div class="card">
<h2 class="card-title">Hello</h2>
</div>
.card {
background: white;
/* No padding, no border — nothing blocking collapse */
}
.card-title {
margin-top: 24px;
/* This margin escapes through .card's top edge */
/* The card appears to have margin-top: 24px */
/* But the space is ABOVE the card background, not inside it */
}
Fixes:
/* Fix 1: Add padding to the parent (most common) */
.card { padding: 24px; }
.card-title { margin-top: 0; }
/* Fix 2: Add a border (even transparent) */
.card { border-top: 1px solid transparent; }
/* Fix 3: Use overflow */
.card { overflow: hidden; }
/* Fix 4: Use display: flow-root (creates BFC) */
.card { display: flow-root; }
| What developers do | What they should do |
|---|---|
| Expecting width: 100% + padding to fit inside the container With content-box (default), padding is added outside the width | Use box-sizing: border-box so width includes padding and border |
| Expecting margin-top: 20px + margin-bottom: 20px = 40px gap between siblings Margin collapse is intentional: it keeps typographic rhythm consistent | Adjacent vertical margins collapse — the larger margin wins |
| Setting margin-top on the first child expecting space inside the parent Without border/padding/BFC, child margins escape through the parent | Use padding on the parent, or create a BFC with overflow or display: flow-root |
| Assuming margin collapse applies in all layout contexts These contexts establish their own formatting contexts that prevent collapse | Flex items, grid items, floats, and absolutely positioned elements never collapse margins |
- 1Use box-sizing: border-box universally — width includes content + padding + border
- 2Margin is never included in box-sizing calculations
- 3Vertical margins collapse between adjacent siblings, between parent and first/last child, and on empty elements
- 4Margins DO NOT collapse in flex, grid, float, absolutely positioned, or overflow contexts
- 5To prevent parent-child collapse: add padding, border, overflow, or display: flow-root to the parent