CSS 2.1 Spec: 8.3.1 Collapsing Margins: Cannot Properly Interpret Special Case: Clarification Sought

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

Could someone explain collapsing margins? I find them extremely annoying

There are many ways to "fix this".

Perhaps the easiest for you would be this:

#like_bar {
overflow: hidden
}

Other ways include:

  • Add some padding
  • Add a border (even border: 1px solid transparent is enough)
  • float the element
  • position: absolute
  • And, like in the snippet above, set overflow to a value other than the default of visible.

You also asked:

what they're used for

A common use case is the <p> tag.

See: http://jsfiddle.net/thirtydot/tPaTY/

Without margin collapsing, certain things would become annoying.

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.

Could someone explain collapsing margins? I find them extremely annoying

There are many ways to "fix this".

Perhaps the easiest for you would be this:

#like_bar {
overflow: hidden
}

Other ways include:

  • Add some padding
  • Add a border (even border: 1px solid transparent is enough)
  • float the element
  • position: absolute
  • And, like in the snippet above, set overflow to a value other than the default of visible.

You also asked:

what they're used for

A common use case is the <p> tag.

See: http://jsfiddle.net/thirtydot/tPaTY/

Without margin collapsing, certain things would become annoying.

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.

What does collapsing width height and margin mean for block level elements?

A collapsed margin is the name given to the instance when margins of two different elements occupy the same space.

Consider the following example:

.box {  height: 50px;  width: 50px;}
.box1 { background: red; margin-bottom: 25px;}
.box2 { background: blue; margin-top: 50px;}
<div class="box box1"></div><div class="box box2"></div>


Related Topics



Leave a reply



Submit