Understanding Margin-Collpasing

How does the margin collapsing of parent and first/last child work?

I threw together this little demo to help demonstrate the way this works:

http://jsfiddle.net/9pq8bm0o/

As you can see, I've made three elements, all nested within each other. The 2 inner containers both have a top margin value of 20px, and the outermost container has a top border (one of the things that is considered a margin separator).

So what does this mean?

Because there is no separation at the top of the two child elements, there is only 20px of space in between the outermost container and BOTH of the two child elements... the inner-most child has had it's margin collapse. Conversely, that margin that is there exists within the outermost container simply because of that border.. if you remove the border, all three elements will share the same 20px of margin which will be outside of all three containers.

So why is it like this?

The best way to think about margin collapsing is like this:
Asking for a margin on an element will ensure that it has that much margin at it's top, and nothing more (unless it's forced to have more).. So looking at my example, Does the middle .parent element have 20px of space above it? Yes, it does. Does the innermost child .child have 20px of space above it? Yes, it also does... so the margin rule is being applied correctly. It doesn't matter where that space lives, as long as it is there.

Imagine that there was a border around the .parent element, but the margin was still displayed the way it is without, and then ask those same questions.. Does the .parent element have the space? Yes, but does the .child element? No, it no longer would, because there would not be 20px of space in between it and the border that is now sitting above it... So, in reality, the space does not collapse, so that both of those questions can be answered as a "yes".

I hope that helps.. it's a little less of a direct answer to your question, and more of the theory behind how it works, so to put things a bit more plainly:

tl;dr

Margin, unlike padding, is meant to add space outside of elements. Because of this, margin will always collapse to the highest parent element whenever possible, to ensure that space is always "outside". Because it is outside of the element, it can count towards multiple different elements, as they all share that "outside" space.

Collapsed margin in CSS

The margins of #empty collapse through, resulting in a 20px collapsed-through margin. This collapsed-through margin collapses with the 10px bottom margin of the first paragraph, and the 10px top margin of the last paragraph. This results in a 20px gap between the first and last paragraphs, since the collapsed-through margin is larger than either of their margins and therefore swallows them both.

Your observation is correct: #empty, when its collapsed through, is rendered with its top margin. From the spec:

  • [...] The position of the element's top border edge is the same as it would have been if the element had a non-zero bottom border.

Note that the positions of elements that have been collapsed through have no effect on the positions of the other elements with whose margins they are being collapsed; the top border edge position is only required for laying out descendants of these elements.

The position that "would have been if the element had a non-zero bottom border" is the position of the element if the element's margins did not collapse through, since having a non-zero bottom border blocks the margins from collapsing through.

How works margin collapsing with min-height?

That version of the Specification is probably a bit confusing. You can check the updated version (2.2) where the main rule is:

both belong to vertically-adjacent box edges, i.e. form one of the following pairs:

...

  • bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height

Then the rule you quoted will become:

The bottom margin of an in-flow block box with a 'height' of 'auto' collapses with its last in-flow block-level child's bottom margin, if:

  • the box has no bottom padding, and
  • the box has no bottom border, and
  • the child's bottom margin neither collapses with a top margin that has clearance, nor (if the box's min-height is non-zero) with the box's top margin.

The min-height is no longer there.


As a side note, the rule you quoted is an implication of the main rules listed above where there is nothing about min-height so having a margin collapsing in case of min-height different from auto won't be a surprise for me if it happens (even if it's non intuitive).

UPDATE

A related old question Margin collapsing with floated element, why there is an extra margin added?. It's now an irrelevant one since we cannot reproduce the behavior but margin-collapsing was occuring in all the cases even if the min-height was bigger than the childs height.


I guess the vertically-adjacent box edges is the key to understand what is happening. If a min-height make both elements no more adjacent then no margin collapsing will occur (which is logical), otherwise you will have margin collapsing (like in your example).

What is the point of CSS collapsing margins?

The general meaning of "margin" isn't to convey "move this over by 10px" but rather, "there must be 10px of empty space beside this element."

I've always found this is easiest to conceptualize with paragraphs.

If you just gave paragraphs margin-top: 10px and had no margins on any other elements, a series of paragraphs would be spaced beautifully. But of course, you'd run into trouble when placing another element underneath a paragraph. The two would touch.

If margins didn't collapse, you'd hesitate to add margin-bottom: 10px to your previous code, because then any pair of paragraphs would get spaced 20px apart, while paragraphs would separate from other elements by only 10px.

So vertical margins collapse. By adding top and bottom margins of 10px you're saying, "I don't care what margin rules any other elements have. I demand at least 10px of padding above and below each of my paragraphs."

Why horizontal margin doesn't collapse as vertical margin?

Horizontal margins never get the chance to collapse as the rules that govern margin collapsing mean that they can never satisfy the conditions.

In CSS, the adjoining margins of two or more boxes (which might or might not be siblings) can combine to form a single margin. Margins that combine this way are said to collapse, and the resulting combined margin is called a collapsed margin.

Collapsing margins (http://www.w3.org/TR/CSS21/box.html#collapsing-margins)

Adjoining boxes can only be block-level boxes:

Two margins are adjoining if and only if:

  • both belong to in-flow block-level boxes that participate in the same block formatting context

Collapsing margins (http://www.w3.org/TR/CSS21/box.html#collapsing-margins)

And margins only collapse if they are not floated or positioned absolutely:

  • Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
  • Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do not collapse with their in-flow children.
  • Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).

Collapsing margins (http://www.w3.org/TR/CSS21/box.html#collapsing-margins)

This means that block-level boxes can never be positioned on the same line horizontally (as block-level boxes will automatically start on a new line by default) and satisfy the margin collapsing conditions at the same time.

In theory, inline boxes could satisfy the conditions but as they are not block-level the rules do not apply to them at all.

In an inline formatting context, boxes are laid out horizontally, one after the other, beginning at the top of a containing block. Horizontal margins, borders, and padding are respected between these boxes.

Inline formatting contexts (http://www.w3.org/TR/CSS21/visuren.html#block-formatting)

That said, the simple reason why they don't collapse is that W3C said so:

Horizontal margins never collapse.

Collapsing margins (http://www.w3.org/TR/CSS21/box.html#collapsing-margins)

Margin Collapse for Adjacent siblings

First, the examples below only work in Gecko based browsers like Firefox on Windows or Android. Chrome/Webkit has a long history of implementing clearance incorrectly.


I think that statement is a misinterpretation of the specification. What the specification actually says is

Two margins are adjoining if and only if:

both belong to in-flow block-level boxes that participate in the same block formatting context and no line boxes, no clearance, no padding and no border separate them

So what causes clearance to have an effect here? It's not the clearance of the latter sibling, but the clearance of an intervening element.

Consider this example.

.container {  overflow:auto;  border:2px solid;}span {  float:left;  width:100px;  height:100px;  background:red;  opacity:0.2;}.container > div {  height:60px;  margin:20px 0;  background:blue;}b {  display:block;  clear:left;}
<p><strong>View this in Firefox</strong></p><div class="container">  <span></span>  <div></div>  <b></b>  <div></div></div>

Margin collapsing with floated element, why there is an extra margin added?

Before I start, the issue of scrollbars being rendered in all browsers but Firefox is a separate issue from what is being asked about here. Firefox does not collapse margins between a parent element and its children when the parent's min-height results in the margins not being adjoining. It's also a known spec violation in Firefox that's being worked on and yet to be fixed.

Now, on to the issue at hand. From section 9.5.1 (on floats):


  1. A floating box's outer top may not be higher than the top of its containing block. When the float occurs between two collapsing margins, the float is positioned as if it had an otherwise empty anonymous block parent taking part in the flow. The position of such a parent is defined by the rules in the section on margin collapsing.

The last sentence in this quote is awkward, but "the rules" refer (and link) to the definition of collapsing through. While the specific text that you cite from that section is relevant, it doesn't explain why the floats respect the margin of the in-flow div.

This does:

If the top and bottom margins of a box are adjoining, then it is possible for margins to collapse through it. In this case, the position of the element depends on its relationship with the other elements whose margins are being collapsed.

  • [...]

  • Otherwise, either the element's parent is not taking part in the margin collapsing, or only the parent's bottom margin is involved. The position of the element's top border edge is the same as it would have been if the element had a non-zero bottom border.

Note the last sentence. Having a non-zero bottom border cancels margin collapsing, as you know. This means that the floats are positioned as if the bottom margins of the in-flow div and the body element did not collapse, resulting in the floats appearing to respect the bottom margin of the in-flow div.

How do I tell that the floats specifically respect the bottom margin of the in-flow div and not the collapsed margin? By giving body a larger bottom margin than that of the in-flow div and observing that it does not affect the position of the floats:

html {
background: red;
}

body {
margin: 0;
margin-bottom: 100px;
min-height: 100vh;
background-color: green;
}

div {
min-height: 50px;
background-color: pink;
margin-bottom: 50px;
}
.l {
width:45%;
height:50px;
float:left;
margin:0;
}
.r {
width:45%;
height:50px;
float:right;
margin:0;
}
<div></div>
<div class="l"></div>
<div class="r"></div>

Margin collapse and clearance

Yes. Take a look at the below snippet [JSfiddle] in either Firefox or IE.¹

.case { width:200px; background-color:yellow; }
.container { background-color:lightblue; margin-bottom:70px; padding-top:0.01px; }.preface { float:left; height: 58px; width: 100px; border: 1px solid red; }.one .intro { clear: left; margin-top:60px; }.two .intro { clear: left; margin-top:59px; }
<div class="case one">  <div class="container">    <div class="preface">      lorem ipsum    </div>    <div class="intro"></div>  </div>  after</div><hr><div class="case two">  <div class="container">    <div class="preface">      lorem ipsum    </div>    <div class="intro"></div>  </div>  after</div>

Margin collapsing in flexbox

Margin collapsing is a feature of a block formatting context.

There is no margin collapsing in a flex formatting context.

3. Flex Containers: the flex and inline-flex display
values

A flex container establishes a new flex formatting context for its
contents. This is the same as establishing a block formatting context,
except that flex layout is used instead of block layout. For example,
floats do not intrude into the flex container, and the flex
container’s margins do not collapse with the margins of its contents.



Related Topics



Leave a reply



Submit