Command Palette

Search for a command to run...

Hectal
PHASE 5Beginner ~26 min· topic 3 of 8

Topic 5.3

Margins in Depth

In one line

Margins can be auto (to centre a block), negative (to pull boxes closer or overlap them), and vertical margins between blocks collapse into one, which explains most mystery gaps in CSS.

Think of it like this

Two people standing in a queue who each want "personal space". One wants 1 metre, the other wants 2 metres. They don't stand 3 metres apart; they stand 2 metres apart, because the bigger wish already covers the smaller one. That's margin collapsing: touching vertical margins share the space instead of adding up.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

auto margin
margin: auto lets the browser fill the leftover space. On both sides of a block, it centres the block.
Negative margin
A margin below zero. It pulls the box (or its neighbour) closer, and can make boxes overlap.
Margin collapsing
Two touching vertical margins becoming one, the size of the larger, instead of adding up.
Normal flow
The default layout: blocks stacked top to bottom, inline content in lines. See Topic 6.1.
Block formatting context (BFC)
A self-contained layout area. Margins inside it don't collapse with margins outside it, and it contains floats.
flow-root
A display value that makes a normal block which also starts a new BFC.
Block direction
The direction blocks stack in: top to bottom for English text.

Step by step

01Centre a block with auto margins

A block is as wide as its parent unless you limit it. Give it a max-width and auto left and right margins: the browser divides the leftover space in two.

style.csswhole filecss
.page {
  max-width: 600px;
  margin-inline: auto; /* same as margin-left: auto; margin-right: auto; */
}

02Watch two margins collapse

A heading with margin-bottom: 30px sits above a paragraph with margin-top: 20px. The gap between them is 30px, the larger of the two, not 50px.

Watch two margins collapsediagram
Rendering diagram…

03See a child's margin escape its parent

A card has a coloured background and an h2 inside with margin-top: 20px. You expect a 20px coloured strip above the heading. Instead the gap appears above the card, outside its background, because the child's margin collapsed through the parent.

style.csswhole filecss
.card { background: lavender; }   /* no border, no padding */
.card h2 { margin-top: 20px; }   /* this margin escapes above .card */

04Stop the escape

Separate the margins: give the parent some padding-top, a border, or make it a new formatting context with display: flow-root. Now the 20px stays inside the card.

style.csswhole filecss
.card {
  background: lavender;
  display: flow-root; /* new block formatting context */
}

05Use a negative margin on purpose

An avatar that overlaps the bottom of a banner: pull it up by half its height with a negative top margin. The text after it moves up too, because the avatar now takes less vertical space.

style.csswhole filecss
.avatar {
  width: 64px;
  height: 64px;
  margin-top: -32px; /* half of its height */
}

06Pick a margin habit

Many teams only use margin-bottom (or only margin-top) for vertical spacing, so siblings never have two margins to collapse, and use gap inside flex and grid containers.

Try it yourself

  1. 1

    Predict the collapsed gap

    In the collapsing example, change .b to margin-top: 50px, then to margin-top: -10px. Predict the gap each time before running (the larger wins: 50; mixed signs are summed: 30 + −10 = 20).

  2. 2

    Stop the escape with padding

    Remove display: flow-root from .root and instead give it padding-top: 1px. Predict the child's distance from the parent's top (20 + 1 = 21), then run.

  3. 3

    Push with one auto

    In the first example, change .right to margin: 8px auto 0 0. Which side does it move to now?

Code & diagrams

Auto margins centre a block New tab

The 160px of leftover space is split in two when both sides are auto, and all goes to one side when only one is.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

centred: left gap 80 right gap 80
pushed right: left gap 160 right gap 0
Margin collapsing, measured New tab

In the plain parent, the blue background starts right at the child: the child's 20px margin collapsed with the parent's 10px and escaped above it.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

gap between A and B: 30
plain parent: child sits 0 px below the parent's top
flow-root parent: child sits 20 px below the parent's top
Negative margin: an avatar overlapping a banner New tab

Preview

Press Run to see the page.

Console

Nothing logged yet.

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Try to centre vertically with auto margins

Give a child inside a 300px-tall block parent margin: auto and expect it to sit in the middle.

terminal
$ the page shows
── what you'll see ──
The child is centred left-to-right but stuck at the top

Break #2

The mystery gap above a header

Put an <h1> (which has a default top margin) as the first child of a coloured <header> with no padding.

terminal
$ the page shows
── what you'll see ──
A white strip appears above the coloured header, outside its background

Myth vs fact

Myth

Margins always add up: 30px + 20px = 50px.

Fact

Vertical margins between blocks in normal flow collapse to the larger one (30px). Horizontal margins, and margins of flex, grid, inline-block, floated and absolutely positioned boxes, do add up.

Myth

margin: auto centres an element both ways.

Fact

In normal flow it only centres horizontally, and only for a block with a width smaller than its parent. Vertical centring with auto margins needs Flexbox, Grid or absolute positioning with inset: 0.

Myth

Negative margins are a bug or a hack.

Fact

They're valid CSS with defined behaviour. Use them for deliberate overlaps or to cancel a parent's padding, but prefer clearer tools (gap, positioning, grid overlap) when they fit.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Collapsing is resolved per spec (CSS 2.1 §8.3.1): positive margins take the max, negative take the min, mixed are summed as max(positive) + min(negative). Several margins can collapse together in a chain (parent top, first child top, grandchild top) into one.

  • ▸

    margin-trim lets a container trim the margins of its first and last children, but it isn't supported in every major browser yet (check caniuse.com). Until it is, use :first-child { margin-top: 0 } or the owl selector * + * { margin-top: 1em } (spacing only between siblings).

  • ▸

    Percent margins resolve against the containing block's inline size (width in horizontal writing) on all four sides, like padding.

  • ▸

    When a parent's height is auto, its bottom margin collapses with its last child's bottom margin; giving it a min-height other than zero or a set height stops that bottom collapse.

Remember this

  1. 1

    margin: auto on the left and right of a block with a set width splits the leftover horizontal space equally, so the block is centred in its parent: .page { width: 600px; margin: 0 auto; } (or max-width for a box that can shrink on phones). Only one side auto pushes the box to the other side: margin-left: auto moves it to the right. In normal block layout, margin-top: auto and margin-bottom: auto compute to 0, so they don't centre vertically (they do in Flexbox and Grid, Phases 7 and 8).

  2. 2

    Margins can be negative. A negative margin-top or margin-left pulls the box up or left, on top of whatever is there. A negative margin-bottom or margin-right pulls the next box closer. Negative margins are useful for small overlaps (an avatar that pokes out of a card header) or to let a child break out of its parent's padding, but they make layouts harder to reason about, so use them sparingly.

  3. 3

    Margin collapsing happens only to vertical (block-direction) margins of block boxes in normal flow. When a bottom margin touches a top margin with nothing between them, the two become one margin, the size of the larger. A paragraph with margin-bottom: 30px above one with margin-top: 20px gives a 30px gap, not 50px. If one margin is negative, they are added (30 + −10 = 20); if both are negative, the most negative wins.

  4. 4

    There are three cases where margins collapse. Adjacent siblings: the bottom margin of one block and the top margin of the next. Parent and first or last child: if the parent has no top border, no top padding and nothing inline before the child, the child's top margin escapes through the parent and becomes the parent's margin (the same for the bottom). Empty blocks: a block with no height, padding, border or content collapses its own top and bottom margins together.

  5. 5

    Collapsing stops when something separates the margins: a border or padding on the parent, inline content (text) in between, a set height or min-height (for the bottom case), or a new block formatting context (BFC). A BFC is a self-contained layout area: margins inside don't collapse with margins outside. You create one with display: flow-root (Topic 5.4), overflow other than visible (Topic 5.6), or by making the parent a flex or grid container.

  6. 6

    Margins never collapse horizontally, never between flex or grid items, never on floated or absolutely positioned boxes, and never on inline-block boxes. This is one reason many developers now space items with gap in Flexbox and Grid, or only use margin in one direction (for example, only margin-bottom), so collapsing never surprises them.

Explain it without notes

01

What is margin collapsing and when does it happen?

02

Why does margin: 0 auto centre a block, and what does it need to work?

03

A child's top margin shows up above its parent's background. Why, and what are three ways to fix it?

04

Give two legitimate uses of negative margins and one risk.

05

In which situations do margins never collapse?

Practice

01

Centre a .notice that is at most 500px wide in its parent, then log the computed margin-left when the parent is 700px wide.

02

Two paragraphs: the first has margin-bottom: 24px, the second margin-top: -8px. Both are 30px tall blocks. Log the gap between them.

03

Fix a .card whose first child h2 margin escapes, without changing the h2. Log the distance from the card's top to the h2's top.

Trade-offs

  • ↔

    Margin collapsing is handy for text (a heading's bottom and a paragraph's top don't double up), but surprising in components. Using gap or one-direction margins avoids it, at the cost of not getting the automatic "take the larger" behaviour.

  • ↔

    Negative margins give quick overlaps without positioning, but they tie elements' sizes together; changing the avatar's height means changing the margin too.

Done when you can

  • I can centre a block horizontally with auto margins and explain why it works.

  • I can predict the gap between two blocks with collapsing margins, including negative ones.

  • I can spot a child margin escaping its parent and fix it three ways.

  • I can use a negative margin deliberately for an overlap.

  • I know where margins never collapse (flex, grid, inline-block, floats, horizontal).