Margin Collapsing with Floated Element, Why There Is an Extra Margin Added

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 collapsing with floated elements

In neither IE6 nor the standards browsers are the float's margins collapsing with its siblings. This is correct as per the quoted standard.

The difference in renderings is caused by IE6's interpretation of which margins flow together. (If you give each div a background colour it makes it easier to see what is happening here.)

Two or more adjoining vertical margins of block boxes in the normal flow collapse

With the normal flow defined thus:

Since a float is not in the flow, non-positioned block boxes created before and after the float box flow vertically as if the float didn't exist.

That is, they flow together and have adjoining vertical margins which may collapse. IE6 (and IE7 in Quirks Mode) gets this wrong, and thinks that the float breaks up the flow, causing no collapsing to occur.

With the general confusion surrounding vertical margins and collapsing, compounded by the still-extant browser bugs, I'd recommend avoiding vertical margins and using padding instead wherever possible.

Why do CSS floats add margins to child elements?

By default there is some margin-top and margin-bottom set from the browser on h2 and p tags, and according to MDN - Mastering margin collapsing

The margins of adjacent siblings are collapsed (except when the later sibling needs to be cleared past floats).

...

If there is no border, padding ... The collapsed margin ends up outside the parent.

...

Margins of floating and absolutely positioned elements never collapse.

Why is this non-float margin collapsing with a float?

Never mind, I think I found it myself. Looks like the following assumption in my question was wrong (told you I don't fully grok the CSS float model):

The fact that the clearing element itself is not floated shouldn't be relevant.

In section 9.5.2, which describes the clear property, it says:

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.

Then the amount of clearance is set to the greater of:

  1. The amount necessary to place the border edge of the block even with the bottom outer edge of the lowest float that is to be cleared.
  2. The amount necessary to place the top border edge of the block at its hypothetical position.

And further down that section, it says:

When the property is set on floating elements, it results in a modification of the rules for positioning the float. An extra constraint (#10) is added:

  • The top outer edge of the float must be below the bottom outer edge of all earlier left-floating boxes (in the case of 'clear: left'), or all earlier right-floating boxes (in the case of 'clear: right'), or both ('clear: both').

(Emphasis mine. Note that "outer edge" is synonymous with "margin edge", as described in section 8.1.)

Essentially, this means if the clearing element is not floated and its top margin alone does not push it far enough away from the floats, then the element is pushed just enough for its top border to sit right underneath the bottom margin of the float being cleared. While it would appear as if its top margin was collapsing with the bottom margin of the float, in reality the top margin doesn't have any meaningful effect because it doesn't reach the border edge of the clearing element.

Otherwise if the clearing element is floated, then its top margin is taken into account, so to speak (as consistent with the rules stated in 8.3.1).

And as I write this, I recall that in-flow non-floating elements are positioned as if the floats were never there to begin with, because floats are taken out of the normal flow. In other words, any top margin that is set on a non-floating clearing element does not take into account any floats, regardless of whether it is enough for clearance. For example, when both clear and float are set to none on the last element, it sits flush with the edges of its container in the exact same position as the first floating element, albeit behind it (note that the borders on the container block margin collapse between it and the container).

Lastly, the fact that clearance is introduced is actually not relevant in this specific situation, because clearance blocks margin collapse only when the element's margins would normally have collapsed had its clear property been set to none. Since we're talking about floats here, margin collapse should indeed never happen normally, and so whether or not the last element has clearance isn't relevant (not directly, anyway).

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

Why does float: left fix this margin issue?

The reason for all this relates to vertical margins collapsing under certain circumstances.

This is what is happening here:

case 1 - parent is floating:

When the parent element is floating the vertical margins of each child element get recognized as being inside the parent element - kind of if you would set a padding on the parent element instead. Since the parent has no background-color and both children have the very same margin top it appears that there is no margin-top at all.

If you set a background color for the parent you will see that the top-margin is being applied for both children and you see an offset to the top border of the parent element.

case 2 - parent is not floating

When the parent is not floating, the vertical margin of each child that is within document flow (in this case the one <p> not floating) gets pushed outside of their parent element. The margin of the floating child elements (<p class="menu-header-box">) however is treated the same way as if the parent was floating itself - it is being recognized as being inside the parent container (see case1 above). This is where your "weird margin" is coming from. You can check this easily when looking at this in the developer tools in your browser or by applying a background-color to the parent element. Since there are only two child elements and the first one is floating, only the second one determins the height of the parent. Because of this you will only see the parents background-color appear on the left and right but not on the top and bottom as you saw in case 1.

margin issue on non-floated elements

Check Demo

CSS Margin Collapsing

float:left; or display: inline-block solves the above issue

Let’s explore exactly what the consequences of collapsing margins are, and how they will affect elements on the page.

The W3C specification defines collapsing margins as follows:

In this specification, the expression collapsing margins means that
adjoining margins (no non-empty content, padding, or border areas, or
clearance separate them) of two or more boxes (which may be next to
one another or nested) combine to form a single margin.

In simple terms, this definition indicates that when the vertical margins of two elements are touching, only the margin of the element with the largest margin value will be honored, while the margin of the element with the smaller margin value will be collapsed to zero.1 In the case where one element has a negative margin, the margin values are added together to determine the final value. If both are negative, the greater negative value is used. This definition applies to adjacent elements and nested elements.

There are other situations where elements do not have their margins collapsed:

  1. floated elements
  2. absolutely positioned elements
  3. inline-block elements
  4. elements with overflow set to anything other than visible (They do
    not collapse margins with their children.)
  5. cleared elements (They do not collapse their top margins with their
    parent block’s bottom margin.) the root element

This is a difficult concept to grasp, so let’s dive into some examples.



Related Topics



Leave a reply



Submit