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 width and height properties (or by the content itself, if those are left to their default of auto).
  • Padding — transparent space between the content and the border. Padding takes on the element’s background-color or background-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

  1. Box generation. During layout, the rendering engine creates a box for every element that isn’t display: none, based on its computed display value (block, inline, flex item, grid item, etc.).
  2. 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 are auto.
  3. Adding padding and border. Under content-box (the default), padding and border widths are added outward from the content box. Under border-box, the engine instead subtracts padding and border from the specified width/height to find the content box size, clamping at zero if padding+border exceeds the declared size.
  4. Resolving percentages. A subtlety worth knowing: percentage values for width, padding, and margin are all calculated relative to the width of the containing block — even padding-top/padding-bottom and margin-top/margin-bottom use the container’s width, not its height. This is why padding-top: 100% is a classic trick for creating a square or aspect-ratio box.
  5. 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.
  6. Applying min/max constraints. Finally, min-width, max-width, min-height, and max-height are 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-box globally (typically via *, *::before, *::after { box-sizing: border-box; }) at the top of your stylesheet — it makes width/height behave predictably and is standard practice in virtually every modern CSS reset.
  • Prefer gap on 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, and margin-block instead of physical left/right/top/bottom when 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/grid gap.

Practice Exercises

  • Exercise 1: Write a rule for .box using box-sizing: content-box with width: 200px, padding: 10px, and border: 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-box and predict how the content area’s width changes while the total rendered width stays 200px.
  • Exercise 3: Create two adjacent <p> elements each with margin: 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/height apply only to the content area — padding and border are added on top, increasing the total rendered size.
  • box-sizing: border-box makes width/height include 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.