Home/Learn/HTML, CSS & Responsive/The Box Model and Spacing

The Box Model and Spacing

Beginner
CSS Fundamentals

Every element is a box with content, padding, border and margin. Two rules — border-box sizing and single-direction margins — remove most sizing surprises.

Overview

The box model is the first thing everyone learns and the source of the first thing everyone gets wrong: a width of 300px with 20px of padding produces a 340px element, because the default sizing applies width to the content box only. Setting box-sizing: border-box globally makes width mean what people expect. The second recurring surprise is margin collapsing, where adjacent vertical margins merge into one — behaviour that looks like a bug until you know it exists, and that modern gap-based layout mostly avoids.

border-box

The one line every stylesheet starts with, and why.

Set border-box globally, once
/* Default: content-box. width applies to the CONTENT only. */
.card { width: 300px; padding: 20px; border: 2px solid }
/* Rendered width = 300 + 40 + 4 = 344px. */

/* border-box: width includes padding and border. */
*, *::before, *::after { box-sizing: border-box }
.card { width: 300px; padding: 20px; border: 2px solid }
/* Rendered width = 300px, with 256px of content. */

/* This is why 'width: 50%' with padding overflows its parent under
   the default and behaves under border-box. Set it once, globally,
   and never think about it again. */

/* The four layers, outside in:
     margin   — space OUTSIDE, transparent, can collapse, not in width
     border   — the visible edge
     padding  — space INSIDE, takes the background
     content  — the text or child boxes                          */

/* Padding vs margin: background reaches into padding, not margin */
.button { padding: .5rem 1rem }     /* clickable, coloured */
.button { margin: .5rem 1rem }      /* space around, not clickable */

Margin Collapsing

Adjacent vertical margins merge. Three cases, and how to stop it.

Collapsing, and using gap instead
/* 1. Siblings: the gap is the LARGER margin, not the sum */
.a { margin-bottom: 30px }
.b { margin-top: 20px }
/* gap = 30px, not 50px */

/* 2. Parent and first/last child: the child's margin escapes */
.parent { }                       /* no padding, no border */
.child  { margin-top: 40px }      /* pushes the PARENT down 40px */

/* 3. Empty elements collapse their own top and bottom margins */

/* It happens only VERTICALLY, and never inside flex or grid,
   or across a border, padding, overflow or a new formatting
   context. So any of these stop it: */
.parent { padding-top: 1px }
.parent { display: flow-root }    /* the clean, purpose-built fix */
.parent { display: flex }

/* The modern answer is to avoid relying on margins for layout
   spacing at all — use gap, which never collapses: */
.stack { display: flex; flex-direction: column; gap: 1rem }

/* And prefer single-direction margins so the source of a gap is
   never ambiguous */
.prose > * + * { margin-block-start: 1rem }   /* the "owl" */

Sizing and Overflow

Constraints beat fixed values, and what to do when content does not fit.

max-width, aspect-ratio, min(), overflow
/* Prefer constraints to fixed sizes */
.card { max-width: 60ch }         /* readable line length */
.avatar { width: 4rem; aspect-ratio: 1 }
.modal { width: min(90vw, 480px) }         /* never wider than the screen */
.hero { min-height: 50vh }

/* Intrinsic sizing keywords */
width: min-content     /* as narrow as the longest word allows */
width: max-content     /* as wide as the content wants, no wrapping */
width: fit-content     /* max-content, capped by the available space */

/* Overflow — the default is to spill visibly */
overflow: visible | hidden | scroll | auto | clip
.table-wrap { overflow-x: auto }

/* A long unbroken string (a URL, a token) is the usual culprit */
.break { overflow-wrap: break-word }
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis }
.clamp { display: -webkit-box; -webkit-line-clamp: 3;
         -webkit-box-orient: vertical; overflow: hidden }

/* overflow: hidden also creates a new formatting context and kills
   position: sticky inside it — a very common accidental breakage. */

Key Points to Remember

  • 1box-sizing: border-box makes width include padding and border, which is what people expect — set it globally
  • 2Adjacent vertical margins collapse to the larger value, and a child's margin can escape its parent
  • 3Flex and grid gap never collapses, which is why gap-based spacing avoids the whole problem
  • 4Prefer constraints — max-width, min(), aspect-ratio — over fixed pixel sizes
  • 5overflow: hidden creates a formatting context and silently breaks position: sticky inside it

Interview Questions

Sign in to ask Aria
1

What does box-sizing: border-box change?

Easy
2

What is margin collapsing and how do you prevent it?

Medium
3

Why might position: sticky stop working inside a container?

Hard

Ask Aria about The Box Model and Spacing

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…