Every HTML element rendered on a page is treated by the browser as a rectangular box. The CSS Box Model defines how that box is structured — four nested layers that control an element’s total size and spacing. Understanding the box model is the single most foundational concept for mastering CSS layout. In this lesson you will learn all four layers, how they interact, and how to visualise them using browser DevTools.
The Four Layers of the Box Model
From innermost to outermost, every box is made of:
| Layer | CSS Property | Description |
|---|---|---|
| Content | width / height |
Where text, images, or child elements live |
| Padding | padding |
Transparent space inside the border, around the content |
| Border | border |
A visible (or invisible) line wrapping padding + content |
| Margin | margin |
Transparent space outside the border, between elements |
content-box vs border-box
| box-sizing | What width includes | Computed Total Width |
|---|---|---|
content-box (default) |
Content only | width + padding-left + padding-right + border-left + border-right |
border-box |
Content + padding + border | Exactly the value of width — no surprises |
Visualising the Box in DevTools
| Browser | Open DevTools | Box Model Location |
|---|---|---|
| Chrome / Edge | F12 or Ctrl+Shift+I | Elements → Computed tab → Box Model diagram |
| Firefox | F12 | Inspector → Computed → Box Model section |
| Safari | Cmd+Opt+I | Elements → Computed → Box section |
outline: 2px solid red to any element during development to visualise its bounds. Unlike border, an outline never affects layout or the box model — it is purely visual and perfect for debugging.box-sizing: content-box means a width: 300px element with padding: 20px and border: 2px actually renders at 344px wide. Always apply box-sizing: border-box globally to make sizing intuitive and predictable.Basic Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Box Model Demo</title>
<style>
/* Global border-box reset — add to every project */
*, *::before, *::after {
box-sizing: border-box;
}
body {
font-family: system-ui, sans-serif;
padding: 32px;
background: #f8fafc;
}
/* content-box comparison */
.box-content {
box-sizing: content-box; /* default — padding ADDS to width */
width: 300px;
padding: 24px;
border: 3px solid #ef4444;
margin: 0 0 24px;
background: #fee2e2;
color: #7f1d1d;
font-size: 0.875rem;
}
/* border-box — padding INCLUDED in width */
.box-border {
box-sizing: border-box; /* total = exactly 300px */
width: 300px;
padding: 24px;
border: 3px solid #4f46e5;
margin: 0 0 24px;
background: #ede9fe;
color: #1e1b4b;
font-size: 0.875rem;
border-radius: 8px;
}
/* Debug outline — no layout impact */
.box-border {
outline: 2px dashed #94a3b8;
outline-offset: 8px;
}
</style>
</head>
<body>
<div class="box-content">
<strong>content-box (default)</strong><br>
width: 300px + padding: 24px + border: 3px<br>
<em>Actual rendered width = 354px</em>
</div>
<div class="box-border">
<strong>border-box</strong><br>
width: 300px (padding + border included)<br>
<em>Actual rendered width = exactly 300px</em>
</div>
</body>
</html>
How It Works
Step 1 — content-box Adds Padding and Border on Top
With the default box-sizing: content-box, setting width: 300px reserves 300px for content only. Padding (24px × 2 = 48px) and border (3px × 2 = 6px) are added on top, making the total rendered width 354px — often breaking layouts.
Step 2 — border-box Absorbs Padding and Border Inside width
With box-sizing: border-box, the browser subtracts padding and border from the stated width to calculate the content area: 300 − 48 − 6 = 246px of actual content space. The rendered box stays exactly 300px — what you declare is what you get.
Step 3 — Padding Fills With Background Color
Padding is transparent to the layout but takes the element’s background-color. This creates the visual breathing room you see between the text and the border. Padding is part of the element’s clickable/interactive area.
Step 4 — Border Wraps Padding and Content
The border ring sits between padding and margin. It can be coloured, dashed, dotted, or invisible (border: none). In border-box mode, its width is subtracted from usable content space rather than added to total width.
Step 5 — Outline for Debugging Does Not Affect Layout
The outline-offset: 8px creates a gap between the element’s border edge and the debug outline ring. This is purely cosmetic — it adds no width to the element, does not move siblings, and does not appear in printed output unless specified.
Real-World Example: Profile Card Layout
<div class="profile-card">
<div class="avatar">AR</div>
<div class="profile-info">
<h2 class="profile-name">Alex Rivera</h2>
<p class="profile-role">Full-Stack Developer</p>
<p class="profile-bio">
Building web apps with React and Node.
Coffee enthusiast and open-source contributor.
</p>
<a href="#" class="profile-btn">View Portfolio</a>
</div>
</div>
*, *::before, *::after { box-sizing: border-box; }
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f1f5f9;
font-family: system-ui, sans-serif;
margin: 0;
}
.profile-card {
width: 360px;
background: #ffffff;
border-radius: 16px;
border: 1px solid #e2e8f0;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.07);
padding: 32px; /* inner breathing room */
margin: 24px; /* space from page edges */
text-align: center;
}
.avatar {
width: 72px;
height: 72px;
border-radius: 50%;
border: 3px solid #6366f1;
background: #ede9fe;
color: #4f46e5;
font-size: 1.4rem;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 16px;
}
.profile-name {
font-size: 1.25rem;
color: #0f172a;
margin: 0 0 4px;
}
.profile-role {
font-size: 0.875rem;
color: #6366f1;
font-weight: 600;
margin: 0 0 12px;
}
.profile-bio {
font-size: 0.9rem;
color: #64748b;
line-height: 1.6;
margin: 0 0 20px;
}
.profile-btn {
display: inline-block;
padding: 10px 24px; /* vertical + horizontal inner spacing */
background: #6366f1;
color: white;
text-decoration: none;
border-radius: 8px;
font-size: 0.875rem;
font-weight: 600;
border: 2px solid transparent;
transition: background 0.2s;
}
.profile-btn:hover { background: #4f46e5; }
Common Mistakes
Mistake 1 — Forgetting the global box-sizing reset
❌ Wrong — elements grow beyond their stated width:
/* No reset — default content-box */
.card { width: 300px; padding: 24px; border: 2px solid; }
/* Actual rendered width = 300 + 48 + 4 = 352px */
✅ Correct — always add this at the top of every stylesheet:
*, *::before, *::after { box-sizing: border-box; }
.card { width: 300px; padding: 24px; border: 2px solid; }
/* Actual rendered width = exactly 300px */
Mistake 2 — Using margin for internal spacing
❌ Wrong — margin is outside the border; background color does not fill it:
.btn { margin: 12px 20px; background: #4f46e5; color: white; }
/* Clickable area does not include the margin space */
✅ Correct — use padding for internal spacing inside the element:
.btn { padding: 12px 20px; background: #4f46e5; color: white; }
/* Background and click area both cover the padded region */
Mistake 3 — Leaving debug outlines in production code
❌ Wrong — outline left in shipped CSS causes visual artifacts:
.card { outline: 2px solid red; } /* forgotten debug remnant */
✅ Correct — use a temporary utility class; remove before shipping:
/* Temporary debug helper — remove before deploy */
.debug * { outline: 1px solid rgba(255, 0, 0, 0.3); }
Quick Reference
| Layer | Property | Key Notes |
|---|---|---|
| Content | width, height |
Use border-box so padding does not expand total size |
| Padding | padding, padding-top etc. |
Takes background color; part of interactive area |
| Border | border, border-width etc. |
Shorthand: width style color — all three required |
| Margin | margin, margin-top etc. |
auto centers block elements horizontally |
| box-sizing | box-sizing: border-box |
Set globally with *, *::before, *::after |
| Debug outline | outline: 1px solid red |
No layout impact — safe for debugging |