Margin Collapse and Clearance

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>

how `clear` prevent margin collapsing?

This ensures that a clear prevents any following elements from overlapping the floats.

Let's start with floats and clear for now. Floats can overflow their parent:

<div style='border:1px solid green;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
That red box overflows!
</div>

If we add a clearing div, it never will. A clear is like saying nothing else can flow above this line:

<div style='border:1px solid green;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
<div style='clear:both;'></div>
<!-- Anything down here will not overlap the floats -->
</div>

However, margin collapsing breaks things a little, because following elements can collapse 'through' something, going all the way up to the very top of the top margin. Let's have a quick excursion into certain aspects of margin collapsing.

Self collapsing hacks

In general, margin collapsing applies to any top margin which is directly touching any bottom margin.

That includes an elements own top/bottom margins too. This is called self-collapsing, and margin collapsing happens repeatedly. Here's a quick example of both of these things together:

<div style='margin-top:30px; margin-bottom:30px;'></div>
<div style='margin-top:30px; border:1px solid black;'>
The gap above me is only 30px, not 90!
</div>

That first div entirely self-collapses, resulting in a computed space of 30px, then the second div collapses into that too, keeping the space at just 30px.

Ok, so we've now got a rough idea of what self-collapsing is. Now let's start trying to abuse that with a self-collapsing clearing div:

<div style='border:1px solid green;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
<div style='clear:left;margin-top:90px;margin-bottom:90px;'></div>
I'm after the clear and look, no 90px gap!
</div>

The margin is still there though. It actually runs 90px upwards over the floats.

Next, imagine there was no text after it, and the parent had a bottom margin. By our margin collapsing rules, it should collapse upwards. Sibling elements might even collapse 'through' it, all the way up to the top. We don't want that, because it would cause some unwanted overlapping.

This part of the specification blocks this behaviour. Let's break the spec's language down to make that clearer:

If the top and bottom margins of an element with clearance are adjoining

This is describing a self-collapsing element which has cleared a float.

its margins collapse with the adjoining margins of following siblings

It's fine for other margins to collapse into it, but..

That resulting margin does not collapse with the bottom margin of the parent block.

..the very bottom margin must not collapse upwards, because that would result in our awkward overlapping case.

Here's an example of the case where the rule is applied:

<div style='border:1px solid green;'>
<!-- A parent block with a bottom margin, and no border -->
<div style='margin-bottom:50px;'>
<div style='float:left;background:red;height:100px;width:40px;'></div>
<!-- A self collapsing cleared div -->
<div style='clear:left;margin-top:50px;margin-bottom:50px;'></div>
<!-- The parents bottom margin is adjacent to our collapsed margin,
but it gets blocked from collapsing upwards. We see a gap here -->
</div>
</div>

Adding some text into that clearing div makes it no longer self collapse, but its bottom margin then safely collapses with the bottom margin of the parent instead.

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.

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>

Margins collapse when clear:both exists

The bottom margin of an in-flow block-level element always collapses
with the top margin of its next in-flow block-level sibling, unless
that sibling has clearance.

clear: both doesn't necessarily mean the element has clearance. According to this section, if there is nothing floating, there is probably nothing clearing either:

Computing the clearance of an element on which 'clear' is set is done
by first determining the hypothetical position of the element's top
border edge. This position is where the actual top border edge would
have been if the element's 'clear' property had been 'none'.

If this hypothetical position of the element's top border edge is not
past the relevant floats, then clearance is introduced, and margins
collapse according to the rules in 8.3.1.

EDIT 1: I created a case where the element gets real clearance calculated, and got the same results. So I guess I missed the point here too. There is also the possibility you found a rendering bug :)


The top margin of an in-flow block element collapses with its first
in-flow block-level child's top margin if the element has no top
border, no top padding, and the child has no clearance.

The border here needs to be on the element itself, not on the child as in your example.

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).

Do vertical margins collapse reliably and consistently across all browsers?

It's hard to give a definitive answer to this question because it's very broad. "All browsers" is a lot of browsers. There could always be some fringe browsers you never heard of that handle this differently. It all depends on how the browser's CSS rendering engine was written.


That said, any browser that wants to be taken seriously will try to adhere the W3C specs, which have the following to say about margin collapsing:

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.

Adjoining vertical margins collapse, except:

  • Margins of the root element's box do not collapse.
  • If the top and bottom margins of an element with clearance are adjoining, its margins collapse with the adjoining margins of following siblings but that resulting margin does not collapse with the bottom margin of the parent block.

Horizontal margins never collapse.

Source: Box Model (w3.org)


I figured it'd be a nice addition to just put your code to the test in as many browsers as possible. I made a test page page of your html (slightly modified), with an absolutely positioned 50px high block that should fit right in between the collapsed margin, to make it easier to spot a difference:

http://files.litso.com/playground/margin.html

Then I ran this through browsershots.org to get screenshots of how browsers would display this piece of HTML:

http://browsershots.org/http://files.litso.com/playground/margin.html#
(I have no idea how long this will stay cached, but I guess you could always just run it again)

Interestingly, the positioning of the blue block is a few pixels off in a bunch of the screenshots. You can still tell the margins are collapsed correctly, but I do wonder what exactly the problem is with the positioning.

The only browsers that don't seem to collapse the margin correctly are Dillo 3.0.2 and Links 2.7 on Debian 6.0, neither of which seem to listen to padding or margin properties at all (nor to the absolute positioning for that matter). They would mess up your layout no matter what, and you shouldn't worry about it. People use browsers like these for a specific reason, and seeing your page exactly as you intended it to be seen is not one of them.


TL;DR: Yes, it's safe to say that all browsers collapse these elements reliably and consistently.



Related Topics



Leave a reply



Submit