CSS The CSS Box Model
Every single element the browser renders is treated as a rectangular box. Understanding how that box is built — from the actual content outward through padding, border, and margin — is the single most important concept in CSS layout. Get the box model wrong and elements overflow their containers, spacing looks inconsistent, and layouts break in ways that seem mysterious until you see the math the browser is doing underneath.
This lesson walks through exactly how the box model works, how the browser computes the final size of an element, the difference between content-box and border-box sizing, and the surprising rules around margin collapsing that trip up even experienced developers.
Overview / How It Works
The box model describes four nested regions, from the inside out:
- Content box — where text and child elements actually appear. Its size is controlled by the
widthandheightproperties (or by the content itself, if those are left to their default ofauto). - Padding — transparent space between the content and the border. Padding takes on the element’s
background-colororbackground-image. - Border — a line (or nothing) drawn around the padding. It sits exactly at the edge where the background stops being painted.
- Margin — fully transparent space outside the border that pushes other elements away. Margin never has a background and can collapse with adjacent margins (explained below).
By default, the CSS specification defines width and height as applying only to the content box. This is the box-sizing: content-box behavior, and it is the initial value for every element unless a stylesheet changes it. That means when you write width: 300px on an element that also has padding and a border, the element’s total rendered footprint is larger than 300px — the padding and border are added on top. This surprises almost every beginner at least once, which is why the alternative box-sizing model, border-box, is so widely used (covered in the examples below).
The rendering engine also treats boxes differently depending on their outer display type. Block-level boxes (default for elements like div, p, section) take up the full width of their containing block by default, stack vertically, and fully respect width, height, and all four margins. Inline boxes (default for span, a, strong) flow with text, ignore width and height entirely, and only respect horizontal (left/right) padding and margin visually — vertical padding and border are painted but don’t push surrounding line boxes apart. Inline-block boxes (display: inline-block) are a hybrid: they flow inline with text but respect width, height, and all four margins like a block box. Flex items and grid items follow their own layout algorithms but are still fundamentally built from the same content/padding/border/margin box.
Syntax
The box model isn’t a single property — it’s a group of related properties that all interact. Here are the core ones:
| Property | Purpose | Shorthand order |
|---|---|---|
box-sizing |
Chooses whether width/height apply to the content box (content-box, default) or the content+padding+border box (border-box) |
single value |
width, height |
Sets the size of the content box (or the full box, under border-box) |
— |
padding |
Space between content and border | top right bottom left (clockwise from top) |
border |
Line drawn around the padding | width style color (can also target one side: border-top, etc.) |
margin |
Space outside the border, separating this box from others | top right bottom left (clockwise from top) |
Shorthand properties like padding and margin follow the same TRBL (top-right-bottom-left) pattern. One value applies to all four sides, two values are vertical horizontal, and four values are explicit. For example, padding: 10px 20px; means 10px top/bottom and 20px left/right.
Examples
Example 1: The default content-box model
.card {
width: 300px;
padding: 24px;
border: 4px solid #2b6cb0;
margin: 16px;
background-color: #ebf8ff;
}
Applied to <div class="card">Hello box model</div>.
Result: The light-blue background and content area are 300px wide (the declared width), but because this element uses the default content-box sizing, the padding and border are added on top of that. The visible box (background + border) actually renders at 300 + 24 + 24 + 4 + 4 = 356px wide. A 16px transparent margin then separates this box from whatever sits next to it — the margin itself has no background color, so you’d see the page’s background color through that gap.
This is the crucial gotcha: the width you write is not the width you see on screen once padding and border are involved.
Example 2: Switching to border-box sizing
* {
box-sizing: border-box;
}
.card {
width: 300px;
padding: 24px;
border: 4px solid #2b6cb0;
margin: 16px;
background-color: #ebf8ff;
}
Result: The rendered box, including its padding and border, is exactly 300px wide — box-sizing: border-box tells the browser that padding and border eat into the declared width rather than adding to it. The content area shrinks to accommodate them: 300 − 24 − 24 − 4 − 4 = 244px of actual room for content. The 16px margin still sits outside all of this, unaffected by box-sizing (margin is never included in either box-sizing mode). This is why applying box-sizing: border-box to the universal selector * is one of the most common lines in any real-world CSS reset — it makes width mean what most developers intuitively expect.
Example 3: Margin collapsing between siblings
p {
margin-top: 24px;
margin-bottom: 24px;
}
.wrapper {
border: 1px solid #999;
}
Applied to <div class="wrapper"><p>First paragraph.</p><p>Second paragraph.</p></div>.
Result: You might expect 48px of space between the two paragraphs (24px bottom margin of the first, plus 24px top margin of the second), but the browser renders only 24px. This is margin collapsing: when two block-level boxes’ vertical margins touch with nothing between them (no padding, border, inline content, or clearance), the two margins merge into a single margin equal to the larger of the two (or their sum, following specific rules, if either is negative). Horizontal margins never collapse — only vertical (top/bottom) margins in normal document flow do.
How It Works Step by Step / Under the Hood
- Box generation. During layout, the rendering engine creates a box for every element that isn’t
display: none, based on its computeddisplayvalue (block, inline, flex item, grid item, etc.). - Content sizing. The engine determines the content box’s dimensions — either from an explicit
width/height, or by measuring the intrinsic size of text/replaced content when those properties areauto. - Adding padding and border. Under
content-box(the default), padding and border widths are added outward from the content box. Underborder-box, the engine instead subtracts padding and border from the specifiedwidth/heightto find the content box size, clamping at zero if padding+border exceeds the declared size. - Resolving percentages. A subtlety worth knowing: percentage values for
width,padding, andmarginare all calculated relative to the width of the containing block — evenpadding-top/padding-bottomandmargin-top/margin-bottomuse the container’s width, not its height. This is whypadding-top: 100%is a classic trick for creating a square or aspect-ratio box. - Adding margin and checking for collapse. Margin is placed outside the border. Before finalizing vertical spacing, the engine checks whether adjacent margins qualify to collapse (siblings, or a parent and its first/last child with no separating border/padding/content), and if so, replaces the pair with a single collapsed margin.
- Applying min/max constraints. Finally,
min-width,max-width,min-height, andmax-heightare applied to clamp the computed size, regardless of box-sizing mode.
Common Mistakes
Mistake 1: Forgetting box-sizing and getting overflow
.box-wrong {
width: 100%;
padding: 20px;
border: 2px solid #333;
}
Why it’s wrong: With the default content-box sizing, width: 100% makes the content area exactly as wide as the parent — then 40px of padding and 4px of border are added on top of that, pushing the total width beyond the parent’s boundary. This is one of the most common causes of unexpected horizontal scrollbars and elements that appear to “leak” out of their containers.
.box-fixed {
width: 100%;
padding: 20px;
border: 2px solid #333;
box-sizing: border-box;
}
Adding box-sizing: border-box makes the 100% width include the padding and border, so the element stays exactly within its parent’s bounds.
Mistake 2: Expecting a parent’s margin to stack with its child’s margin
.parent {
margin-top: 40px;
}
.parent .child:first-child {
margin-top: 20px;
}
Why it’s wrong: If .parent has no border, no padding, and no inline content above the child, its top margin and the first child’s top margin collapse into one — the visible gap above .parent ends up being 40px (the larger of the two), not 60px (their sum), which surprises anyone expecting the margins to add together.
.parent {
margin-top: 40px;
padding-top: 1px;
}
.parent .child:first-child {
margin-top: 20px;
}
Adding even 1px of padding-top (or a border) to .parent creates separation between the two margins, preventing the collapse so both margins take effect independently.
Best Practices
- Apply
box-sizing: border-boxglobally (typically via*, *::before, *::after { box-sizing: border-box; }) at the top of your stylesheet — it makeswidth/heightbehave predictably and is standard practice in virtually every modern CSS reset. - Prefer
gapon flex or grid containers over relying on margins for spacing between children — it avoids margin-collapsing surprises entirely and doesn’t require special-casing the first or last item. - Remember that percentage padding/margin (including vertical ones) resolves against the containing block’s width, not height — useful for aspect-ratio tricks, confusing if you forget it.
- Use browser DevTools’ box model diagram (in the Elements/Inspector panel) to visually confirm the actual computed content, padding, border, and margin sizes rather than guessing.
- Consider logical properties like
padding-inline,padding-block,margin-inline, andmargin-blockinstead of physicalleft/right/top/bottomwhen building layouts that need to support right-to-left languages. - Don’t fight margin collapsing with hacks like empty
<div>spacers — understand it, or sidestep it cleanly with padding, border, or a flex/gridgap.
Practice Exercises
- Exercise 1: Write a rule for
.boxusingbox-sizing: content-boxwithwidth: 200px,padding: 10px, andborder: 5px solid black. Calculate the total rendered width by hand, then check it against the box model panel in your browser’s DevTools. - Exercise 2: Rewrite the same rule using
box-sizing: border-boxand predict how the content area’s width changes while the total rendered width stays 200px. - Exercise 3: Create two adjacent
<p>elements each withmargin: 30px 0;, then add a single-pixel top border to their shared parent. Predict — then verify — how the border changes the collapsing behavior between the parent and the first paragraph.
Summary
- Every element is a box made of, from the inside out: content, padding, border, and margin.
- By default (
box-sizing: content-box),width/heightapply only to the content area — padding and border are added on top, increasing the total rendered size. box-sizing: border-boxmakeswidth/heightinclude padding and border, which is far more predictable and is a near-universal reset in modern CSS.- Margin is always outside the border, is always transparent, and is the only box-model region that can collapse with an adjacent margin.
- Adjacent vertical margins between block boxes in normal flow collapse into a single margin (the larger of the two) unless separated by padding, border, or inline content.
- Percentage values for width, padding, and margin — including vertical ones — are calculated against the containing block’s width.
