-2

Update 2020: the below behavior is no more visible in the recent browsers


Let's start with the following situation:

html {
  background: red;
}

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

div {
  min-height: 50px;
  background-color: pink;
  margin-bottom: 50px;
}
<div></div>

The body is defined with a min-height:100vh and we have a scroll bar that allow us to see the html. Here we are having a margin-collapsing, the margin of the div is collapsed with the body margin and thus create this space after the body and the scroll bar.

If we refer to the specification we have this case:

Two margins are adjoining if and only if:

...

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

the div is the last in-flow element and body has height auto as we only specified min-height.


Now let's add more elements that may be affected by this margin and keep the rules of margin-collapsing. The only way to do this is to add floated elements to keep our div always the last in-flow element.

Here is the new code:

html {
  background: red;
}

body {
  margin: 0;
  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>

As we can clearly see, we still have margin collapsing (because of the scroll) AND the floated elements are also pushed down by the same amount of the margin.

So my question is: why such behavior?


My understanding of margin-collapsing is that at the end we will have only one margin applied somewhere. By adding new elements, I am expecting to have one of this two situations:

  1. Adding the floated elements will somehow cancel the margin-collapsing (this cannot be the case as we are not breaking any rule)
  2. The floating elements will not get affected by the margin as this one collapsed with body margin and thus moved/applied to the body. (This is a logic case for me)

In the specification I also found this complex statement:

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.

I understand from the above that the other elements get not affected by the margin-collapsing and thus keep their initial position which explain why the floated elements are pushed down. (I am not sure if this is the case by the way)


If this is the explanation then it's a bit confusing and illogical for me. I added one margin and I end up having two margins that are clearly visible?

So why such behavior? Or maybe I missed something in the specification and we are facing more than a simple margin-collapsing?


Important notice: Before answering, please note that I am not looking for a fix to this or how to avoid this. I know at least 5 ways to cancel margin-collapsing (padding, overflow, border, flexbox,etc..).I am looking to understand why such thing happen.


For the reference: this started by this question where @Alohci highlighted this in my answer and after few comments we both didn't get convinced

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Are you talking about the behaviour of a particular browser? Because here on Firefox Mac I don't see a scroll bar and also no space after body... – Johannes Feb 14 '18 at 00:22
  • @Johannes ah i was testing with Chrome .. am gonna check the others too – Temani Afif Feb 14 '18 at 00:23
  • @Johannes right, it's only visible on Chrome :/ ... i tested with Firefox and IE11. Well this can be a bug then, but refering to the specification it should be the same for all .. or maybe i missread something – Temani Afif Feb 14 '18 at 00:28
  • 1
    @Johannes i will try to produce a similar scenario with Firefox then – Temani Afif Feb 14 '18 at 00:32
  • I haven't tried it in Chrome, but what you describe is a strange behaviour... – Johannes Feb 14 '18 at 00:51
  • 2
    @Johannes: Only Firefox does not generate the scrollbar. The scrollbar is generated in all other browsers, including IE11. That may or may not be related to [another issue covered about 9 years ago](https://lists.w3.org/Archives/Public/www-style/2008Sep/0081.html) (before CSS2.1 graduated to REC), except this involves height: auto on the root element... – BoltClock Feb 14 '18 at 16:31

1 Answers1

11

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>
Community
  • 1
  • 1
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • 1
    OK so my explanation was almost perfect then, because i didn't say that the float was affected by the collapsing margin but by the margin of the div (i was sure about this because i also adjsuted both margin to see what happens) ... maybe it wasn't clear on my question. I only had a doubt about which part of the spec describe that the float respect the margin applied to them intial even if there is collpasing .. BUT the real question is why this ? my page show two margin and i on apply one. – Temani Afif Feb 14 '18 at 19:14
  • I know it's the specification ... but it sound a bit illogical for me ... as you can see in my question, i expect one of two logic situation ... why there is the specification something that allow us to visually see two margin while we added only one – Temani Afif Feb 14 '18 at 19:15
  • now with my question and your answer we have the needed parts to explain the whole behavior including the issue with FF ... but is something like this logic for you ? you are not suprised about such things ? – Temani Afif Feb 14 '18 at 19:18
  • @Temani Afif: The only situation in which a float can end up between two collapsing margins is when the float is a following sibling of an in-flow element and that in-flow element's bottom margin is collapsing with that of their parent. My guess is that it's specced this way because a float can never be affected by its containing block's margins, so the float has no other choice but to respect its in-flow sibling's margin as it normally would if margin collapsing didn't take place. – BoltClock Feb 15 '18 at 10:35
  • 1
    @Temani Afif: IMO the spec should be changed to match Firefox's behavior because the current behavior, while logical, is counter-intuitive. *Then* it would make sense for the floats to respect their in-flow sibling's bottom margin, whether or not the floats overflow the parent as a result of the parent having or not having a min-height constraint. – BoltClock Feb 15 '18 at 10:42
  • *so the float has no other choice but to respect its in-flow sibling's margin as it normally would if margin collapsing didn't take place* : this is exactly what i don't like :) ... as i am understanding everything BUT if we forget all the technical parts, as a coder i don't like ending with two margins (even if there is easy way to avoid them) ... so i was expecting to have a line in the spec that says : "if this happen, then we omit one of the margin" – Temani Afif Feb 15 '18 at 10:45
  • *the spec should be changed to match Firefox's behavior because the current behavior, while logical, is counter-intuitive* : this exactly what i meant :) i find this totally counter-intuitive even if everyhing is very well explained in the spec – Temani Afif Feb 15 '18 at 10:46