Margin Collapse for Adjacent Siblings

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>

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

How to disable margin-collapsing?

There are two main types of margin collapse:

  • Collapsing margins between adjacent elements
  • Collapsing margins between parent and child elements

Using a padding or border will prevent collapse only in the latter case. Also, any value of overflow different from its default (visible) applied to the parent will prevent collapse. Thus, both overflow: auto and overflow: hidden will have the same effect. Perhaps the only difference when using hidden is the unintended consequence of hiding content if the parent has a fixed height.

Other properties that, once applied to the parent, can help fix this behaviour are:

  • float: left / right
  • position: absolute
  • display: inline-block / flex / grid

You can test all of them here: http://jsfiddle.net/XB9wX/1/.

I should add that, as usual, Internet Explorer is the exception. More specifically, in IE 7 margins do not collapse when some kind of layout is specified for the parent element, such as width.

Sources: Sitepoint's article Collapsing Margins

Is there any way to allow CSS margins to collapse through a fieldset boundary?

Yes, the fieldset element establishes the new block formatting context (this behavior was first implemented in the browsers, so the spec incorporated this feature as part of "expected default rendering").

Unfortunately, I don't know any way to "undo" this with CSS, except completely removing the fieldset element's box by setting it to display:contents, which currently only gives the desired result in Chrome with the "Experimental Web Platform features" flag turned on (Firefox, although implemented display:contents back in 2015, hasn't updated its implementation to work for "unusual elements" like form controls according to the recent addition to the spec yet).



Related Topics



Leave a reply



Submit