CSS from the ground up

The box model

Every element is a box: content, padding, border, margin — and the one line that makes the arithmetic sane.

Part 4 of 12

Beginner14 min readBack to Courses

Every element is a box

The browser gives every element four layers, from the inside out: the content, the padding between the content and the edge, the border around it, and the margin that pushes other boxes away. Backgrounds paint underneath the padding and the border, which is why padding looks like part of the element and margin does not.

The difference matters the moment something looks wrong. Cramped text wants padding; a cramped layout wants margin. Reaching for the wrong one moves the wrong thing.

Not every box is laid out the same way. A block element takes the full width available and starts on a new line; an inline element — a link, a span, a word inside a paragraph — flows along the text and ignores width, height and vertical margins. Padding and borders still paint around inline text, but they do not push the lines apart, so a link with generous padding can overlap the line above it. When a box refuses to take a size, check its display before changing the number.

The four layers, named

.card {
  width: 20rem;
  padding: 1.25rem;          /* inside the border */
  border: 1px solid #d4d4d8; /* the edge itself */
  border-radius: 12px;       /* the corners */
  margin: 0 0 1.5rem;        /* space to the next block */
}

.card { box-sizing: border-box; }

One line that fixes the arithmetic

By default, width measures the content only — so an element 20rem wide with padding and a border is wider than 20rem, and two of them no longer fit the row you planned.

box-sizing: border-box changes the meaning: the width now includes padding and border, so what you asked for is what you get. Most stylesheets set it on every element at the top of the page, so the whole document agrees — and it costs nothing.

Margin does something else

Two vertical margins touching each other collapse into the larger of the two, rather than adding up. It is why a heading can seem to ignore its margin — the paragraph below brought its own, and only one survived.

The habit that avoids fighting it: give spacing to one side. Space below a block with margin-bottom, or space between elements with gap in a flex or grid container, and there is nothing to collapse.

The edge belongs to the box

The border sits at the edge of the box, and border-radius clips the background to the rounded shape. That is why a card with a radius and a background needs no extra work: the background follows the curve.

An outline, by contrast, is painted outside the border and takes no space in the layout — which is exactly why it is the right tool for a focus ring, and why replacing it with a border shifts everything by a pixel. The same distinction explains a common surprise with box-sizing: a border is part of the box under border-box, while under the default it is added to the width. Two cards with identical width values can differ by two pixels, and the difference appears only once a border is added.

The arithmetic of content-box

With the default box-sizing: content-box, width sets the content area alone. A card with width: 20rem, 1.5rem of padding on each side and a 1px border is 20rem + 3rem + 2px wide on screen. Two of those do not fit a 40rem row, even though each one says 20.

border-box moves the padding and border inside the number: width: 20rem now means the whole visible box, and the content area is whatever is left. The usual reset applies it to every element and both pseudo-elements, because a pseudo-element with padding is a box too.

Where the space goes

Margins sit outside the border and never take the background with them. Two vertical margins between siblings collapse to the larger one, but padding never collapses — it is part of the box. That is also why a child’s top margin can escape a parent with no padding and push the parent down instead.

margin: 0 auto centres a block only when the block is narrower than its container; the auto margins share the leftover space. On an inline element, or a block that already fills the width, there is no leftover to share, so nothing moves.

Three box-model mistakes: setting width: 100% and adding padding without border-box, which overflows the parent by exactly the padding; using margin for space that should be inside the box, so the background stops short of the text; and expecting padding-top: 10% to follow the height, when percentage padding always resolves against the width.

Try these next

  • Put two width: 50% cards in a flex row with a gap, find the overflow, and fix it with box-sizing and a smaller basis.
  • Give a paragraph padding: 1rem and then margin: 1rem, and compare which one moves the background.
  • Centre a 30rem block with margin: 0 auto, then remove its width and watch the centring disappear.

Give the card its layers

The card below has no space inside it, no edge, and no rounded corners. Add all three, and set the box model so its width means the whole box.

Live Preview
Console output will appear here...

3 left to do

  • The card has space inside it
  • The card has an edge
  • The corners are rounded

padding: 1.25rem; for the inside, border: 1px solid #d4d4d8; for the edge, border-radius: 12px; for the corners — and box-sizing: border-box; on the card.

Two cards that fit

The two cards are told to take half the row each, but their padding and borders push them past it. Make the width include both, give each card a real edge, and let the row hold them without overflowing.

Live Preview
Console output will appear here...

3 left to do

  • The width includes padding and border
  • The cards have space inside
  • The cards have an edge

box-sizing: border-box; on the cards, padding: 1.25rem;, and border: 1px solid #d4d4d8;. The row already has a gap, so flex: 1 is a safer width than a full 50%.

When a layout is one or two pixels off, the browser’s box-model view — in the inspector’s Computed panel — draws all four layers with their sizes. It answers in seconds what guessing never does.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Text inside a button is touching its edges. What do you change?