What's the Deal with Collapsible Margins?
Last week, I ran a twitter poll asking the following question - What would be the amount of space between two sibling divs, where the 1st has a margin-bottom of 10px and 2nd a margin-top of 30px?
The result of this poll was that 39% of the 754 people who voted got the correct answer, which was 30px.
[CSS Quiz] What would be the amount of space between two sibling divs, where the 1st has a margin-bottom of 10px and 2nd margin-top 30px? — Ire Aderinokun (@ireaderinokun) June 27, 2017
The reason the resulting margin would be 30px was because of a feature of CSS called “Collapsing margins”.
A Refresh on the CSS Box Model #
Before we get into how collapsing margins work, we need to revisit the box model. As I mentioned in my more in-depth article on Controlling the Box Model, every element in the document tree is a rectangular box made up of four areas - the content area, padding area, border area, and margin area.
The margin area is the space outside an element’s border. Let’s take this .box
element, for example -
.box {
width: 300px;
height: 300px;
padding: 50px;
border: 50px solid grey;
margin: 50px; /* margin area */
}
The margin area of this box is shown striped below:
The margin area is different to the other three areas of the box model because it is not technically part of the element itself. Even if we specify a set margin for an element in our CSS, the actual margin painted on the document can be affected by the other elements within the document, as we will see in the case of collapsing margins.
What is a Collapsed Margin? #
A collapsed margin is what occurs when two block-level elements with meeting vertical margins combine. When this happens, the larger of the two margins (or any if they are equal) is assumed as the single collapsed margin.
Let’s take the example from my Twitter poll. We have two elements; Element A has a bottom margin of 10px, and element B has a top margin of 30px. Here are the two elements shown separately -
If we place these two, block-level, elements one after the other in our markup, this is the result we get -
The margin between the two elements is combined and the resulting “collapsed” margin with a height of 30px is what is left.
When do Margins Collapse? #
As a general rule, adjoining vertical margins between in-flow, block-level, boxes will always collapse. There are four scenarios in which this takes place:
Case 1: The top margins of a parent and it’s first child #
If a top margin is applied to a parent element as well as it’s first in-flow child, those margins may be collapsed together.
.parent {
margin-top: 30px;
height: 150px;
background-color: rgb(200,200,200); /* Grey */
}
.child {
margin-top: 30px;
width: 100px;
height: 100px;
background-color: rgb(250, 219, 92); /* Yellow */
}
Case 2: The bottom margins of a parent and it’s last child #
Similarly to the first example, margins may be collapsed if both a parent and last in-flow child have a bottom margin. However, unlike the top margins, bottom margins will only collapse if the parent has a computed height of auto
.
.parent {
margin-bottom: 30px;
height: auto;
background-color: rgb(200,200,200);
}
.child {
margin-bottom: 30px;
width: 100px;
height: 100px;
background-color: rgb(250, 219, 92);
}
Case 3: The bottom and top margins of siblings #
This is the scenario I mentioned in my poll. If we have two elements, where one has a bottom margin and the second in-flow sibling has a top margin, those margins may be collapsed.
.sibling-one {
margin-bottom: 10px;
width: 100px;
height: 100px;
background-color: rgb(250, 219, 92);
}
.sibling-two {
margin-top: 30px;
width: 100px;
height: 100px;
background-color: rgb(200,200,200);
}
Case 4: Empty elements #
Finally, both the top and bottom margins on a single element may collapse if the element has a computed height of 0
, i.e. if it is an empty element.
<p>This paragraph is before the empty element</p>
<div class="empty"><!-- Empty element --></div>
<p>This paragraph is after the empty element</p>
.empty {
margin-bottom: 30px;
margin-top: 30px;
}
Exceptions #
As with everything, there are a few exceptions to take note of for when margins will not collapse.
Flexbox, Grid, and other non-block-level elements #
Collapsible margins only apply to block-level elements. These are elements that have either of the following values for the display
property: block
, list-item
, or table
. Therefore, flex items, grid items, absolutely positioned items, and other non-block-level elements do not apply.
The root element #
The margins of the root element’s box will never collapse.
Lines boxes, clearance, paddings, and borders #
In the example cases mentioned above, collapsing will not happen if there are line boxes, clearance, paddings, or borders, that stand between the two elements.
Let’s take the first example I mentioned, where the top margin of the parent is collapsed with the top margin of it's first child. If we add a border around the parent element, the margins no longer collapse.
This happens because the margin of the parent is no longer in direct contact with the margin of the child.
Dealing with Collapsed Margins #
Collapsible margins can be a pain if you don't properly understand when they occur. The first step to dealing with or avoiding them is understand exactly which case of collapsible margins we are dealing with.
Collapsed margins that occur because of empty elements or parent/child relationships can't really be avoided. The only way to counteract collapsible margins that occur this way is by inserting something between the elements, for example a border. Otherwise, changing the element's display status to something that is not block-level would be an option.
Collapsed margins that occur because of adjoining sibling elements, on the other hand, can be avoided with a change in CSS writing style. Personally, I prefer to follow Harry Roberts' rule of Single-direction margin declarations, which has other benefits besides helping to avoid collapsible margins.