How works margin collapsing with min-height? - html

I've read in the spec this:
W3C
The bottom margin of an in-flow block box with a 'height' of 'auto' and a 'min-height' of zero collapses with its last in-flow block-level child's bottom margin if the box has no bottom padding and no bottom border and the child's bottom margin does not collapse with a top margin that has clearance.
But as for min-height I can't say that the spec tells us the truth. Here's some example:
* {
margin: 0;
padding: 0;
}
/*parent with min-height*/
.block {
width: 500px;
min-height: 500px;
background: rgba(4, 127, 214, 1);
margin-bottom: 10px;
}
.child_1 {
width: 500px;
height: 250px;
background: yellow;
}
.child_2 {
width: 500px;
height: 250px;
background: yellowgreen;
margin-bottom: 30px;
}
.content {
width: 100%;
height: 50px;
background: pink;
}
<div class="block">
<div class="child_1"></div>
<div class="child_2"></div>
</div>
<div class="content"></div>
This example shows us that margins can collapsing with min-height. Am I right?

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

Related

How would two empty divs with only margins collapse?

How would two empty divs with only margins collapse?
html file:
<div id="div1"></div>
<div id="div2"></div>
CSS File:
#div1 {
margin-top: 10px;
margin-bottom: 20px;
}
#div2 {
margin-top: 10px;
margin-bottom: 30px;
}
Thanks.
The answer is:
You will have only 30px margin at the end
You are facing two kind of margin collpasing. Since each div is empty, its top margin and bottom margin will collapse so for the first one we will have 20px and for the second one 30px. Then those margin will also collapse together and will end with only 30px.
You can verify this by checking the height of the html element and you will see this:
#div1 {
margin-top: 10px;
margin-bottom: 20px;
}
#div2 {
margin-top: 10px;
margin-bottom: 30px;
}
<div id="div1"></div>
<div id="div2"></div>
From the sepcification:
top and bottom margins of a box that does not establish a new block
formatting context and that has zero computed 'min-height', zero or
'auto' computed 'height', and no in-flow children
And
bottom margin of box and top margin of its next in-flow following
sibling
As a side note, the body has also a default margin (8px) collapsing with other other margin following this rule:
top margin of a box and top margin of its first in-flow child
And
bottom margin of a last in-flow child and bottom margin of its parent
if the parent has 'auto' computed height
If you mean to ask what this would look like, you can simply add borders to the divs to give you an idea of what is happening.
Since you've set a margin to both divs, there will be a margin separating the two. However, since the divs are empty they will have no height.
You can check out this jsfiddle: https://jsfiddle.net/rt1y0v4j/
You can also use chrome dev tools to inspect the element by hitting cmd-shift-C on Mac or ctrl-shift-C on Windows/Linux. Hovering over the elements in devtools should give you a visual confirmation that these divs are adhering to the specified margins.

Margin collapsing with floated element, why there is an extra margin added?

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:
Adding the floated elements will somehow cancel the margin-collapsing (this cannot be the case as we are not breaking any rule)
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
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):
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>

Div positioning is changing with and without border

I'm testing some css styles on a blog-like div layout. I used div{border: 1px solid black;} to see the divs and look at its positions.
When this line is in my style, it looks right, but I don't want to have the borders (just had it for development).
As soon as I comment it out everything changes it's positions. Why is this so?
JSFiddle Link
div{border: 1px solid black;} /* Comment this to see the problem */
body{ text-align:center; }
.postTabs{
float:left;
background-color: #c8c8c8;
width: 60px;
height: 38px;
padding: 27px 5px 5px 5px;
border-radius: 50%;
}
.postContent{
padding: 15px 15px 15px 50px;
margin-left: 35px;
margin-top: 36px;
text-align: left;
background-color: #a7a7a7;
}
<div class="postContainer">
<div class="postTabs">asdf</div>
<div class="postContent">
<div class="postBody">adf</div>
</div>
</div>
This is because of collapsing margins:
8.3.1 Collapsing margins
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.
Also the spec states:
Two margins are adjoining if and only if:
both belong to in-flow block-level boxes that participate in the same block formatting context
no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for
this purpose.)
both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
top margin of a box and top margin of its first in-flow child
bottom margin of box and top margin of its next in-flow following sibling
bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height', zero
or 'auto' computed 'height', and no in-flow children
In this case, the first child is floated to a side and it is removed from normal flow. Hence the first in-flow child of the .postContainer container is .postContent element which has a margin-top of 36px.
Since the container doesn't establish a block formatting context, and there's no border, padding between them, the margins would be collapsed into one.
You could prevent that by giving the container:
A padding-top of 1px - for instance - Example here.
A border-top of 1px solid transparent Example here.
An overflow of anything other than visible which creates a new block formatting context - Example Here.
For further info you could refer to the spec.
Check the fiddle here
JS Fiddle
it was happening because of float:left for .postTabs class
.postContainer{
clear: both;
float: left;
width: 100%;
}
This is because of the default box model in CSS. The element is as wide as the width, plus any padding, plus any border.
You may have expected the border to sit inside of the element, but it actually makes it wider (by 2px in your case) and taller (again by 2px).
You can adjust your width and height accordingly, or you can change the box-model being used with the CSS box-sizing: border-box;
In your case, though, you are only applying the border for development purposes, so changing your CSS to support the border will mean changing it all back again later.
Rather than do all that work, use your browser tools. The screenshot below shows the browser tools in Firefox (all browser have similar tools to this). When you highlight the element in the HTML shown in the tools, it shows the outline of the element on the actual page.
This gives you your development view whenever you need it without having to change the code.

A fluid div is pushing a fixed div next to it down. It also isn't filling the expected height

I have two divs next to each other - one fixed, and one fluid. However, whenever I apply a margin-top to a paragraph within the fluid div, it doesn't fill the whole height of the div and also pushes the fixed div next to it down with it. Unfortunately, I cannot use overflow:auto to fix this because I require the fluid div to have overflow: visible for a very specific need. Weird, I know, but I'm sure there must be a solution to this. However I've been trying for hours with no luck.
Here's a demo of the problem I'm having. I've included an explanation within the divs also: http://jsfiddle.net/LejbU/
<div class="left">
<p>This div has a fixed width of 300px.<p>
</div>
<div class="right">
<p class="withMargin">Test</p>
</div>
-
.left {
background-color: yellow;
width: 300px;
height: 100%;
float: left;
}
.right {
background-color: pink;
height: 100%;
margin-left: 320px;
overflow: visible;
}
p {
margin: 0;
padding: 10px;
color: black;
}
p.withMargin {
margin-top: 100px;
margin-bottom: 100px;
}
That's because of the collapsing margins of CSS Box model definition:
CSS 2.1 8.3.1 Collapsing margins
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.
From the definition:
Margins of inline-block boxes do not collapse (not even with their
in-flow children).
So change the display of p.withMargin to inline-block to avoid this behavior.
Demo: http://jsfiddle.net/LejbU/2/
You've fallen victim to collapsing margins (MDN).
Top and bottom margins of blocks are sometimes combined (collapsed) into a single margin whose size is the largest of the margins combined into it, a behavior known as margin collapsing.
In your case:
Parent and first/last child
If there is no border, padding, inline content, or clearance to separate the margin-top of a block with the margin-top of its first child block, or no border, padding, inline content, height, min-height, or max-height to separate the margin-bottom of a block with the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.
There are a couple ways to get around this, I just use padding instead to change the layout as I require without changing other properties I consider more dangerous. In this scenario: http://jsfiddle.net/rgthree/LejbU/1/
p.withMargin {
padding-top: 100px;
padding-bottom: 100px;
}
add this display:inline-block; to p.withMargin
Another solution I found. Create a new div WITHIN the right div and apply the following css to the new div:
.correctblock {
display: inline-block;
width: 100%;
zoom: 1;
}
Example: http://jsfiddle.net/62ueY/

Why does a negative bottom margin on an element decrease the height of parent of that element?

This might be due to margin collapsing and I know about margin collapsing, at least how it affects adjacent elements, but I don't understand how it works on nested elements when negative margins are involved.
For example, in this markup and accompanying CSS:
Markup
<div class="parent">
<div class="child">
Child 1
</div>
</div>
<div class="parent">
<div class="child negative">
Child 1
</div>
</div>
CSS
body {
background: white;
padding: 45px;
}
.parent {
border: 1px solid black;
margin-bottom: 10px;
}
.negative {
margin-bottom: -1px;
}
Live example here.
When I inspect the height of the second .parent div, I notice it is 1 pixel less than the first one. This has happened because of the negative margin on the .negative element inside it. I had a quick look at W3C and couldn't find an explanation for this behavior.
Could someone please explain what's happening here and also provide me with a link to the W3C spec section about it?
This might be due to margin collapsing and I know about margin collapsing, at least how it affects adjacent elements, but I don't understand how it works on nested elements when negative margins are involved.
Section 8.3.1 has all the details. It also covers the behavior of adjoining margins between nested boxes, as well as negative margins.
However, what you're seeing here is not the effect of margin collapse because you have negated it with a border: 1px solid black declaration in your .parent rule. That means having a border there prevents your .parent margin from collapsing with your .child.negative margin altogether.
Rather, this is simply how negative margins work. This is covered in various sections of the visual formatting model, but it's most succinctly and directly addressed in the beginning of Section 11, which summarizes it thus:
Generally, the content of a block box is confined to the content edges of the box. In certain cases, a box may overflow, meaning its content lies partly or entirely outside of the box, e.g.:
...
A descendant box has negative margins, causing it to be positioned partly outside the box.
So what's happening here, instead, is:
The absolute value of the .child.negative element's negative margin is subtracted from the .parent element's actual height (by 1px).
As a result, the .child.negative element itself overflows .parent (because its own height is not changed and the default overflow for any div is visible).
Since margin collapse does not take effect here, the margin-bottom: 10px in your .parent is unaffected. Note that while any subsequent elements in normal flow will be shifted up by 1px, this is mainly due to the negative margin of your .child.negative element; in other words, a side effect of step 1.
And that's all there is to it.
when you are using .negative { margin-bottom: -1px; } then it will moved at the top. see this example.
please refer the following link you understand easily.
http://coding.smashingmagazine.com/2009/07/27/the-definitive-guide-to-using-negative-margins/
body {
background: white;
padding: 45px;
}
.parent {
border: 1px solid black;
margin-bottom: 10px;
min-height: 30px;
}
.negative {
margin-bottom: 20px;
}
conclusion:
For e.g. In your case i have to added min-height:30px to parent class so that it remains fix. if moved only if you add positive margins to negative class. It just because you can see results in above figure that tells all what you need is.
see the cssdesk link click here cssdesk
Hope, it will helps you. Cheers. !!
Margins of first and last elements will apply to outer element when the outer element doesn't have border, padding and content, instead of it self.
In your case, parent node has border, so margin collapsing won't apply in this case. As you have margin-bottom = -1px for the child node inside, when calculate the outer height of the child node will be the height of its content + padding + border-width + margin. So it will be 1px less when measuring from outside. That's why the height of parent node will be 1px less than the upper example. To see it more clearly, you may apply a background to the child node, say yellow, you will find that the child node will overlap the border of the parent node.
But if you remove the border of the parent node, it will be a total different situation.
For instance to explain margin collapsing, say you have
<div style="background-color:black">
<div style="height:10px; background-color:white; margin-top: 10px"></div>
</div>
You will not see a black box of 10px height, as the outer node will be considered to have a 10px margin on top, and the inner one's margin is ignored. And for negative situation, the outer margin will decrease.
Quote from spec:
When two or more margins collapse, the resulting margin width is the maximum of the collapsing margins' widths. In the case of negative margins, the maximum of the absolute values of the negative adjoining margins is deducted from the maximum of the positive adjoining margins. If there are no positive margins, the maximum of the absolute values of the adjoining margins is deducted from zero.
For more info:
https://developer.mozilla.org/en-US/docs/CSS/margin_collapsing