Top margin of first child and bottom margin of last child - html

HTML
<div class="lev1">level1</div>
<div class="lev2wrap">
<div class="lev2">level2</div>
<div class="lev2">level2</div>
<div class="lev2">level2</div>
<div class="lev2">level2</div>
</div>
CSS
.lev1{
background:lightblue;
}
.lev2wrap{
background:gold;
}
.lev2{
background:#999999;
margin:10px 0;
}
Why does the first instance of lev2 not have a top margin of 10px, and why does the last instance of lev2 not have a bottom margin of 10px?
Instead, lev2wrap has a top and bottom margin, but that's not a CSS instruction.
JSFIDDLE

Because of collapsing margins.
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.
Margin collapsing occurs in three basic cases:
Adjacent siblings
Parent and first/last child
Empty blocks
To have the margin of lev2wrap show, add overflow: auto to it:
.lev2wrap {
background: gold;
overflow: auto;
}
jsFiddle example

Related

How works margin collapsing with min-height?

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

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>

Why does any html element require 'padding' & 'border'?

Every html element is a box shape element.
Each box is surrounded by boundaries - padding, border, margin.
--
margin gives white space between two elements.
Why would a box require three boundaries? Would margin that creates white space between any two boxes do not suffice?
Not if you also need a border, or some padding.
Though it is true that both margin and padding create space, there is a difference between where they create space. And that difference is the border.
A border, as the word already implies, is to create a visible border. Padding creates space between said border and the content within. But padding can also be used to create some room around an element when it has a background, for example.
To better illustrate the differences, I'll create a couple of snippets:
This snippet has no border, margin or padding, so no spacing.
.row {
background: red;
}
.column {
background: green;
}
.blue {
background: blue;
}
<div class="row">
<div class="column">
Some text
</div>
<div class="column blue">
Some other text
</div>
</div>
This snippet has margins, giving it some space around the element, which is evident because of the background colors.
.row {
background: red;
}
.column {
margin: 10px;
background: green;
}
.blue {
background: blue;
}
<div class="row">
<div class="column">
Some text
</div>
<div class="column blue">
Some other text
</div>
</div>
This example has both a margin and a border, giving you a wider range of coloring options, as well as more space. Yet, you would be unable to give the different spaces a different color with just a margin.
.row {
background: red;
}
.column {
margin: 10px;
border: 5px solid purple;
background: green;
}
.blue {
background: blue;
}
<div class="row">
<div class="column">
Some text
</div>
<div class="column blue">
Some other text
</div>
</div>
This last example has it all. As you can see, the padding creates space within the box, inside of the border. Added to that, you can also see more of the background color of the element.
.row {
background: red;
}
.column {
margin: 10px;
border: 5px solid purple;
padding: 20px;
background: green;
}
.blue {
background: blue;
}
<div class="row">
<div class="column">
Some text
</div>
<div class="column blue">
Some other text
</div>
</div>
Though you could create just as much space between the elements with margin: 35px; you could not get this (* cough *) beautifully colorful display.
You need a border because sometimes people want a visible border between elements, not white space.
You need padding because people want space between the content and the border and between the border and the next element.
Each one of those properties controls a different aspect of the box.
Margin
The margin clears an area around an element (outside the border). The
margin does not have a background color, and is completely
transparent. The top, right, bottom, and left margin can be changed
independently using separate properties. A shorthand margin property
can also be used, to change all margins at once.
Padding
The padding clears an area around the content (inside the border) of
an element. The padding is affected by the background color of the
element. The top, right, bottom, and left padding can be changed
independently using separate properties. A shorthand padding property
can also be used, to change all paddings at once.
Border
The CSS border properties allow you to specify the style, size, and
color of an element's border.
All three properties together give you great flexibility in styling HTML elements. If you only had margin you would only be able to create space between elements. Plus, padding gives you the ability to create "separation" between elements without collapsing margins.
Here's a good reference for more details: When to use margin vs padding in CSS
Elements don't require to have any of the above. What you see is just an illustration about the box-model of the element which just tells you that there is no margin, padding or border.
Important difference between marginand padding is that margin pushes other elements away from the current element, while padding defines the space between the contents of an element and its' outline.
Border is simply a border. It creates a line as a visual separator between elements, and is not really intended to determine spacing between them.
A good explanation is given on the w3schools website.
Margins and padding have two different uses:
Margin collapse on each other while padding doesn't. Two elements side-by-side, each having margin: 10px will be 10px apart. But if they instead had padding: 10px, the would be 20px apart. Edit I misspoke. I was trying to refer to margin-collapsing, which happens on margin-top and margin-bottom at times. More can be read here: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing; and additional discussion here: When to use margin vs padding in CSS
When applying a border, padding will be applied inside the border, pushing it away from the element. Margins are applied outside the border.
Styling such as background-color will be applied to padding, but not to margin.
With margins, negative values are allowed. Not so with padding.
From MDN:
Padding
The padding area extends to the border surrounding the padding. When the content area has a background, color, or image set on it, this will extend into the padding, which is why you can think of the padding as extending the content. The padding is located inside the padding edge, and its dimensions are the padding-box width and the padding-box height.
Margin
The margin area extends the border area with an empty area used to separate the element from its neighbors. It is the area inside the margin edge, and its dimensions are the margin-box width and the margin-box height.

Why does the margin space between stacked HTML block elements collapse instead of each element asserting its own margin space?

Let's say you have two <p> elements, one stacked on top of the other, and each with 3rem of top and bottom margin.
With this set up, I expect there to be a total of 6rem of white space between the two <p> elements (3rem from the top <p>'s bottom margin, plus 3rem from the bottom <p>'s top margin). However, there is only 3rem of white space between the elements.
Why does the margin space collapse between the block elements instead of each block element holding on to its own margin space?
Here is a codepen that illustrates the problem of collapsed margin space between two paragraphs.
The html of the above codepen example is:
<p class="outline-red">This is a paragraph with top & bottom margin and padding set at 3rem.</p>
<p class="outline-yellow">This is a paragraph with top & bottom margin and padding set at 3rem.</p>
The CSS of the above codepen example is:
body {
margin: 0;
padding: 0 3rem;
background-color: #34495e;
font-family: "Helvetica", sans-serif;
text-align: center;
color: white;
}
.outline-red,
.outline-yellow {
display: block;
margin-top: 3rem;
margin-bottom: 3rem;
padding-top: 3rem;
padding-bottom: 3rem;
}
.outline-red { border: 1px solid red; }
.outline-yellow {border: 1px solid yellow; }
The short answer is, "because that's what the spec says".
But I guess what you're really asking is why does the specification work that way. And I believe it has to do with the semantic difference between margins and padding:
• Padding is part of the element, not to be shared with other elements.
• Margin is not part of the element, but is space shared with other elements.
For example, element backgrounds cover the padding, but do not extend into the margin. Mouse events over the padding are delivered to the element, but events over the margin are not. Etc.
The second margin-top doesn't care about the first margin-bottom space, it only cares about the first <p> element. So the count starts from the position of the first <p> element (without margins).
MDN: Thats just the way it is
the padding property does not collapse.
the margin property vertically (top and bottom) collapses.
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.
Exceptions: float, inline-block, etc.
Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
Margins of elements that establish new block formatting contexts (such as floats and elements with 'overflow' other than 'visible') do
not collapse with their in-flow children.
Margins of absolutely positioned boxes do not collapse (not even with their in-flow children).
Margins of inline-block boxes do not collapse (not even with their in-flow children).
...