Padding is space inside the element, between its content and its border. Margin is space outside the element, between its border and everything else. The consequences of that one-word difference are larger than they look.
The definition takes a sentence and is not why people search for this. What actually causes trouble is that padding is part of the element and margin is not, which means padding takes your background colour and responds to clicks, while margin does neither and, uniquely, collapses into adjacent margins in a way that surprises almost everyone the first time.
So this is the practical difference: what each one does to backgrounds, borders, click targets and layout maths.
Table of contents
- The box, from the inside out
- Margin collapse, which is the real subject
- box-sizing, and the width that is not the width
- Choosing between them
- Logical properties, briefly
- How this fits the rest of the stack
- FAQ
The box, from the inside out
An element is four concentric regions, and knowing the order settles most questions.
+-----------------------------------------+
| margin | <- outside, transparent
| +-----------------------------------+ |
| | border | |
| | +-----------------------------+ | |
| | | padding | | | <- inside, takes background
| | | +-----------------------+ | | |
| | | | content | | | |
| | | +-----------------------+ | | |
| | +-----------------------------+ | |
| +-----------------------------------+ |
+-----------------------------------------+
Three consequences follow directly from that ordering.
- Background extends through padding, not margin. A coloured button with padding has a larger coloured area. The same button with margin has the same coloured area and more space around it.
- Padding is clickable, margin is not. Padding is part of the element, so it registers pointer events. This is why padding is the correct tool for enlarging a small tap target.
- Border sits between them. Padding pushes content away from the border; margin pushes the border away from neighbours.
There is a fourth difference that is easy to forget: padding cannot be negative, and margin can. A negative margin genuinely pulls an element toward or over its neighbour, which is occasionally the right tool and more often a sign that the layout wants restructuring.
Margin collapse, which is the real subject
Vertical margins between adjacent elements do not add. They collapse to the larger of the two.
.a { margin-bottom: 30px; }
.b { margin-top: 20px; }
/* Gap between them: 30px. Not 50px. */
This is defined behaviour, not a bug, and it exists so that a document of paragraphs each with a bottom and top margin has consistent spacing rather than double gaps. It applies only to vertical margins in horizontal writing modes, never to horizontal ones, and never to padding.
There are three collapse cases and the second is the one that produces confused bug reports.
- Adjacent siblings. The bottom margin of one and the top margin of the next collapse into a single gap.
- Parent and first or last child. If a parent has no border, padding or anything else separating it from its child, the child’s top margin escapes and becomes the parent’s. The child appears not to move; the parent moves instead.
- Empty elements. An element with no content, border or padding collapses its own top and bottom margins together.
Case two is the one that produces the classic why is there a gap above my container question. The fix is to give the parent something to separate them: a single pixel of padding, a border, overflow: auto, display: flow-root, or making it a flex or grid container.
/* The modern, side-effect-free way to stop margins escaping. */
.container { display: flow-root; }
display: flow-root exists specifically for this. It establishes a new block formatting context without the scrollbar risk of overflow: auto or the layout changes of flex.
box-sizing, and the width that is not the width
By default, width sets the content box only. Padding and border are added on top, so an element set to 300 pixels wide with 20 pixels of padding either side occupies 340.
This is why almost every stylesheet written in the last decade opens with the same three lines:
*, *::before, *::after {
box-sizing: border-box;
}
With border-box, width includes padding and border, so 300 pixels means 300 pixels on the page. Everything gets easier: a full-width input with padding stops overflowing its container, and percentage widths behave the way people expect.
One thing border-box does not change: margin is still added outside. Two elements at 50% width with any horizontal margin still will not fit on one line. That is the case flex gap was designed for, and it is a better tool than margins for spacing between siblings.
Percentage padding is worth one warning. A percentage padding, vertical or horizontal, resolves against the containing block’s width, not its height. That is surprising, and it is also the basis of the old aspect-ratio hack, which the aspect-ratio property now replaces properly.
Choosing between them
Two rules cover the large majority of decisions.
Use padding when the space belongs to the element. Space between a button’s text and its edge. Space inside a card between the border and the content. Anywhere the background should extend through the space, or the space should be clickable.
Use margin when the space is between elements. The gap between a heading and the paragraph after it. Separation between cards.
The refinement that makes stylesheets maintainable: prefer gap over margin for spacing between siblings in a flex or grid container. A component then owns its internal padding and nothing else, and its spacing from neighbours is decided by the layout that contains it. That separation is why components stop needing per-context margin overrides.
/* The container owns the spacing between children. */
.card-list {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
/* The component owns only its inside. */
.card {
padding: 1.5rem;
border-radius: 8px;
}
This also sidesteps margin collapse entirely, because gap does not collapse. A great deal of layout debugging simply stops happening.
Logical properties, briefly
Modern CSS has direction-aware equivalents that are worth adopting for anything that might be translated.
/* Physical: always left and right, regardless of language direction. */
.old { margin-left: 1rem; padding-right: 2rem; }
/* Logical: start and end follow the writing direction. */
.new { margin-inline-start: 1rem; padding-inline-end: 2rem; }
/* Shorthands for both axes. */
.card { padding-block: 1rem; padding-inline: 1.5rem; }
In a right-to-left language, margin-inline-start becomes the right side automatically. Without logical properties, supporting a right-to-left layout means a parallel stylesheet mirroring every horizontal value, which is exactly the kind of duplication that drifts.
The block and inline shorthands are worth adopting even in a monolingual project, purely because padding-block: 1rem is clearer than padding-top: 1rem; padding-bottom: 1rem and harder to get half-wrong.
How this fits the rest of the stack
Front-end work of this kind has to end up deployed somewhere, and a static build with correct cache headers is usually the cheapest thing in a stack. The RunxBuild hosting calculator shows what a static site costs with 120GB of bandwidth included and $0.10/GB beyond, which for most sites means the layout work is the expensive part and the hosting is not.
Useful related references:
FAQ
What is the difference between margin and padding?
Padding is space inside the element, between its content and its border, so it takes the element’s background and responds to clicks. Margin is space outside the border, between the element and its neighbours, and it is always transparent and not clickable.
Why do my margins not add up?
Vertical margins between adjacent elements collapse to the larger of the two rather than summing. A 30 pixel bottom margin next to a 20 pixel top margin produces a 30 pixel gap. This applies only to vertical margins and never to padding or horizontal margins.
Why is there a gap above my container?
Most likely a collapsed child margin. If a parent has no border, padding or formatting context separating it from its first child, the child’s top margin escapes and applies to the parent instead. Add display: flow-root to the parent to contain it without side effects.
Should I use margin or padding for spacing between elements?
Neither, ideally. In a flex or grid container, use gap: the container owns spacing between children while each component owns only its internal padding. Gap does not collapse and does not require per-context margin overrides, which removes a large class of layout bugs.
What does box-sizing: border-box actually change?
It makes the width property include padding and border rather than only the content box, so an element set to 300 pixels occupies 300 pixels regardless of its padding. Margin is still added outside it. Applying it universally is standard practice in modern stylesheets.