Lesson 10 of 25

CSS Margins and Padding

Padding Fills, Margin Pushes

Both properties create space, and choosing the wrong one is the most frequent cause of layouts that almost work. Padding is space inside the element, between its content and its edge; it carries the background colour and it is part of the clickable area. Margin is space outside the element, holding neighbours away; it is always transparent and it is never clickable.

That difference has a real consequence for touch screens. A navigation link with padding: 12px 16px is comfortably tappable on a phone, because the whole padded area responds to a finger. The same link with margin: 12px 16px looks identical but has a tap target the size of the text alone, and people miss it. Whenever you are making something easier to click, the answer is padding.

The rule of thumb, then: use padding to make an element itself bigger and roomier, and margin to control the distance between separate elements. If you cannot decide, ask whether the space should be the same colour as the element. If yes, it is padding.

Example
/* Padding: bigger tap target, background follows */
.nav a {
  padding: 12px 16px;
  background: #ffffff;
}

/* Margin: distance between separate things */
.card { margin-bottom: 24px; }

/* The clockwise shorthand — top, right, bottom, left */
.a { padding: 16px; }
.b { padding: 16px 24px; }            /* vertical, horizontal */
.c { padding: 16px 24px 32px; }       /* top, sides, bottom */
.d { padding: 16px 24px 32px 8px; }   /* each side named */

/* Logical properties: block = vertical, inline = horizontal */
.e { margin-block: 24px; }            /* top and bottom */
.f { margin-inline: auto; }           /* left and right */
  • Padding is inside the border and takes the background colour
  • Margin is outside the border and is always transparent
  • Only padding enlarges the clickable or tappable area
  • Percentage padding and margin are calculated from the parent's width — even the top and bottom ones
  • margin-block and margin-inline are readable modern shorthands for the vertical and horizontal pairs

margin: 0 auto and What auto Really Means

margin: 0 auto is the standard way to centre a block element horizontally, and it works by asking the browser to share the leftover space equally between the left and right margins. Two conditions have to hold for that to do anything. The element must be a block-level element, and it must have a width smaller than its container — usually via width or max-width. A block with no width already fills the whole line, so there is no leftover space to share and auto resolves to zero.

The other half of that rule is the part people trip over: margin: auto 0 does not centre anything vertically. In normal document flow, an auto vertical margin computes to zero. There is no leftover vertical space to distribute, because a block's height comes from its content rather than from its container. Vertical centring needs flexbox, grid or absolute positioning — all covered in later lessons — and no amount of margin will do it.

A common page shell puts max-width and margin-inline: auto together on a container, with horizontal padding so the content never touches the screen edge on a phone. Those three declarations are the backbone of nearly every site layout, and everything else sits inside them.

Example
/* Horizontal centring — needs a width to work */
.container {
  max-width: 1100px;
  margin: 0 auto;
  padding-inline: 20px;   /* keeps content off the screen edge on phones */
}

/* This does nothing — auto vertical margins compute to 0 */
.not-centred {
  height: 200px;
  margin: auto 0;
}

/* Vertical centring needs a different tool */
.centred {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 60vh;
}
Notes
  • Inside a flex or grid container, margin: auto does work in both directions, because those layouts really do have free space to distribute. margin-left: auto on one flex item is the neatest way to push a single button to the far right of a toolbar.

Margin Collapsing: the Behaviour Nobody Expects

Put two paragraphs one above the other. Give the first margin-bottom: 30px and the second margin-top: 20px. The gap between them is not 50 pixels. It is 30. Vertical margins between adjacent block elements collapse: instead of adding, the two margins merge and the larger one wins. This is not a bug, it is how normal document flow has always been specified — and it is the reason spacing sometimes refuses to increase no matter what you type.

The second form of collapsing is stranger. If a parent has no border, no padding and no content between its own top edge and its first child, the child's top margin escapes the parent and becomes the parent's top margin. So you add margin-top: 40px to a heading inside a card, and instead of the heading moving down inside the card, the entire card moves down by 40 pixels and the heading stays glued to the top. Everybody meets this once and assumes something is deeply broken.

Anything that separates the parent's edge from the child stops it: one pixel of padding, a border, or overflow: hidden on the parent. Better still, avoid the whole situation. Margin collapsing does not happen at all inside flex or grid containers, so a card laid out with display: flex; flex-direction: column; gap: 16px simply never has the problem — which is a large part of why modern layouts feel so much more predictable.

Three more details are worth knowing. Horizontal margins never collapse, only vertical ones. An element with no height, border or padding collapses its own top and bottom margins together into one. And if negative margins are involved, the result is the largest positive margin plus the most negative one, which can produce a smaller gap than either value on its own.

Example
/* Siblings: 30 and 20 give a 30px gap, not 50 */
.first  { margin-bottom: 30px; }
.second { margin-top: 20px; }

/* Parent and child: the child's margin escapes */
.card       { background: #fff; }        /* no padding, no border */
.card h2    { margin-top: 40px; }        /* the CARD moves down */

/* Any of these stop it */
.card { padding-top: 1px; }
.card { border-top: 1px solid transparent; }
.card { overflow: hidden; }

/* Or sidestep it entirely */
.card {
  display: flex;
  flex-direction: column;
  gap: 16px;      /* no collapsing inside flex containers */
}
Watch two margins collapse into one
HTML
<h3>1. Siblings — 30px + 20px is still 30px</h3>
<div class="wrap">
  <p class="a">margin-bottom: 30px</p>
  <p class="b">margin-top: 20px</p>
</div>

<h3>2. Parent and child — the margin escapes</h3>
<div class="wrap">
  <div class="card leaky"><p class="inner">margin-top: 40px on this text</p></div>
</div>

<h3>3. Same card, with one pixel of padding</h3>
<div class="wrap">
  <div class="card sealed"><p class="inner">margin-top: 40px on this text</p></div>
</div>
CSS
body {
  font-family: system-ui, Arial, sans-serif;
  padding: 24px;
  background: #f4f4f7;
}

h3 { font-size: 15px; color: #555555; margin: 24px 0 8px; }

/* The dashed wrapper shows where each block really sits */
.wrap {
  border: 1px dashed #b9b9c6;
  background: #ffffff;
}

.a, .b {
  background: #fdf2fa;
  margin: 0;
  padding: 8px;
}
.a { margin-bottom: 30px; }
.b { margin-top: 20px; }

.card {
  background: #d1039e;
  color: #ffffff;
}

.inner {
  margin: 40px 0 0;
  padding: 8px;
  background: #7a0060;
}

/* leaky: no padding, so the child's margin pushes the whole card down */
.leaky { padding: 0; }

/* sealed: one pixel is enough to stop the collapse */
.sealed { padding-top: 1px; }
Notes
  • Collapsing is also blocked for floated elements, absolutely positioned elements, inline-block elements and anything with an overflow value other than visible. You do not need to memorise that list — remember instead that flex and grid containers never collapse, and reach for them.

Spacing Systems, gap, and Negative Margins

Once a project grows past a few pages, spacing chosen ad hoc starts to show. One card has 18 pixels of padding, the next has 20, a heading has a 22-pixel margin, and nothing quite lines up. The cure is a small scale of allowed values — 4, 8, 12, 16, 24, 32, 48 — stored as custom properties and used everywhere. It removes a decision from every rule you write and makes the whole interface look deliberate.

For gaps between items, prefer gap to margins whenever the parent is a flex or grid container. gap puts space between items only, never before the first or after the last, so you avoid the usual routine of adding a margin to everything and then removing it from the last child. It also cannot collapse, so the number you write is the space you get. Its one limit is that it works in flex, grid and multi-column layouts, and nowhere else — it does nothing on a plain block container.

Negative margins are legal and occasionally the right tool: pulling an image out to the full width of a padded container, or tightening a gap that a component's own margin insists on. They work by reducing the space an element claims, which can make elements overlap. Use them sparingly and with a comment, because a negative margin somewhere unexpected is genuinely hard for the next reader to trace.

Example
/* A spacing scale, defined once */
:root {
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 32px;
}

.card       { padding: var(--space-4); }
.card + .card { margin-top: var(--space-4); }

/* gap beats margins for spacing between items */
.toolbar {
  display: flex;
  gap: var(--space-3);   /* only between, never around */
}

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
}

/* A deliberate negative margin: break out of the container padding */
.container      { padding-inline: 20px; }
.full-width-img { margin-inline: -20px; width: calc(100% + 40px); }
  • Pick a small spacing scale and reuse it instead of inventing numbers
  • gap works in flex, grid and multi-column layouts only
  • gap never collapses and never adds space at the outer edges
  • Prefer spacing an element downwards (margin-bottom) or use gap, so directions do not fight each other
  • Negative margins overlap elements — legitimate, but always worth a comment
Ask AI