Why margins of such a box containing a in-flow box collapse - css

Here are my test code:
.div1 {
width: 100px;
background: red;
padding-top: 0.1px;
border: solid;
}
.div2 {
width: 100px;
margin: 100px;
background: pink;
}
.div3 {
overflow: visible;
}
.div4 {
overflow: hidden;
}
<p>sample 1</p>
<div class="div1">
<div class="div2"><div class="div3"></div></div>
</div>
<hr />
<p>sample 2</p>
<div class="div1">
<div class="div2"><div class="div4"></div></div>
</div>
According to the spec:
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
margins of both div2 may not collapse. However, in sample 2, margins didn't collapse as I thought, but they collapse in sample 1. So why?
According to BoltClock's explanation, margins in sample 1 collapse because the in-flow child(i.e. div3) don't establish a new BFC. I tried another test:
.div1 {
width: 100px;
background: red;
padding-top: 0.1px;
border: solid;
}
.div2 {
width: 100px;
margin: 100px;
background: pink;
}
.div5 {
overflow: visible;
float: left;
/* position: absolute;*/
}
<div class="div1">
<div class="div2"><div class="div5"></div></div>
</div>
With float value other than none, div5 establish a new BFC, so margins of div5 may not collapse accordingly. But they collapse unfortunately.

From the spec:
Note the above rules imply that:
A box's own margins collapse if the 'min-height' property is zero, and it has neither top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto', and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.
Emphasis mine.
In sample 2, div4 establishes a new block formatting context. As a result, its own margins are not adjoining (i.e. it does not meet the condition that you have quoted), and so they cannot collapse. Because div4's margins do not collapse, the margins of its own div2 parent cannot collapse through it. As a result, none of the positive margins in sample 2 collapse.
Floating an element takes it out of the flow, so the margins of div2 in your new test are able to collapse because it has no in-flow children.

This is very interesting question. I never faced with this before, but now, according to some articles I found that some situation prevents margin collapsing:
http://www.sitepoint.com/web-foundations/collapsing-margins/
floated elements
absolutely positioned elements
inline-block elements
elements with overflow set to anything other than visible (They do
not collapse margins with their children.)
cleared elements (They do not collapse their top margins with their parent block’s bottom margin.)
the root element
Apparently overflow prevent margin collapsing not only if it is applied on parent element ;)
From other article I saw, that this effect is used in several micro clearfix.

Related

In CSS, how does overflow interact with float?

I'm confused by the interaction I'm seeing between the overflow property on an element and float on a sibling element. Consider the following:
.div1 {
float: left;
width: 100px;
height: 50px;
margin: 10px;
border: 3px solid #73AD21;
}
.div2 {
border: 1px solid red;
width: 50px;
height: 150px;
}
<h2>Without clear</h2>
<div class="container">
<div class="div1">div1</div>
<div class="div2">div2 - Notice that div2 is after div1 in the HTML code. However, since div1 floats to the left, the text in div2 flows around div1.</div>
</div>
(This example was adapted from this example on w3schools: https://www.w3schools.com/css/tryit.asp?filename=trycss_layout_clear
In this case, dev1 floats to the left of div2, and is within the box for div2 — e.g., the border for div2 extends above and to the left of div1, but the text content of div2 wraps around div1. But also note: because of the width/height set on div2, the text in div2 overflows below.
Now, add overflow:hidden; to div2:
.div1 {
float: left;
width: 100px;
height: 50px;
margin: 10px;
border: 3px solid #73AD21;
}
.div2 {
border: 1px solid red;
width: 50px;
height: 150px;
overflow: hidden;
}
<h2>Without clear</h2>
<div class="container">
<div class="div1">div1</div>
<div class="div2">div2 - Notice that div2 is after div1 in the HTML code. However, since div1 floats to the left, the text in div2 flows around div1.</div>
</div>
Now div2 is entirely to the right of div1 — it's border no longer extends around div1.
Why does adding the overflow:hidden property to div2 change its layout interaction wtih div1 in this way? (Same effect also happens for overflow:auto or overflow:scroll.)
You need to consider the concept of Block formatting contexts where you can read the following:
Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.
So when adding overflow:hidden, the div2 will establish a new BFC then we can read:
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space ref
To make it easy, when an element create a BFC its content will no more interact with the outside world. From the MDN you can read:
Setting overflow: auto created a new BFC containing the float. Our <div> now becomes a mini-layout inside our layout. Any child element will be contained inside it.

What does collapsing width height and margin mean for block level elements?

What does it mean that the width of block level elements can not be collapsed but the height can?
And can you please explain this text from the W3.org specification:
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.
The meaning of the word collapse is causing much of the confusion here.
A collapsed margin is the name given to the instance when margins of two different elements occupy the same space.
Consider the following example:
.box {
height: 50px;
width: 50px;
}
.box1 {
background: red;
margin-bottom: 25px;
}
.box2 {
background: blue;
margin-top: 50px;
}
<div class="box box1"></div>
<div class="box box2"></div>
It's difficult to tell, but that whitespace between the two boxes is only 50px. You might think it should be 75px, because I've specified a margin-bottom of 25px on the top box, and a margin-top of 50px on the bottom box. 25 + 50 = 75, so why is the whitespace only 50px?
Well, margins can't have any content within them; a margin is specifically denoting a lack of content. Considering there is no content to display in a margin, the parser thinks they might as well be combined to optimise space.
The word 'collapsed' comes about because there are technically two different 'segments' of margins existing in the same place at the same time, 'collapsing' in on each other.
Note that this doesn't happen with margin-left and margin-right:
.box {
height: 50px;
width: 50px;
float: left;
}
.box1 {
background: red;
margin-right: 25px;
}
.box2 {
background: blue;
margin-left: 50px;
}
<div class="box box1"></div>
<div class="box box2"></div>
The space above is indeed 75px. This can be a confusing concept to wrap your head around, but it's important to note that it only affects vertical margins. Further information about collapsing margins can be found at CSS Tricks and Mozilla.
It's also important to note that, by default, a block-level element takes up 100% of the width of its parent, but 0% of the height.
Here's an example illustrating this:
.parent {
background: blue;
border: 10px solid purple;
height: 50px;
width: 200px;
}
.child {
background: red;
}
<div class="parent">
<div class="child">Text</div>
</div>
In the above example, I specify both a width and a height on the parent, though I don't specify either on the child. As you can see, the child element inherits the 200px width, but does not inherit the 50px height.
Hopefully this helps clarify that a bit!

Uncollapse a vertical margin in two adjacent elements

There are multiple ways posted here to uncollapse a vertical PARENT margin, but nothing about uncollapsing vertical margins of adjacent elements. The only solution I found was in this answer (back in 2009):
<div style="overflow: hidden; height: 0px; width: 0px;"> </div>
Almost 7 years passed since there. Is some better way to do this (possibly using some CSS3)?
Basically, suppose you have: http://jsfiddle.net/ok2u3o3c/
<div class="one"></div>
<div class="two"></div>
div {
width: 300px;
height: 200px;
}
.one {
margin-bottom: 10px;
background-color: blue;
}
.two {
margin-top: 20px;
background-color: red;
}
What would be the most elegant way to make the distance between these 2 boxes 30px instead of 20px (where the first margin contributes 10px and doesn't collapses)?
Let's start with the relevant documentation explaining the behavior of collapsing margins:
8 Box model - 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.
The following rules apply, which means that there are a things that you can do to prevent the margins from collapsing for sibling elements:
Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children)
Therefore if you float the elements with collapsing margins, they will no longer collapse:
.collapsing-margins {
margin: 100px 0;
background: #f00;
float: left;
width: 100%;
}
<div class="parent">
<div class="collapsing-margins">Element</div>
<div class="collapsing-margins">Element</div>
</div>
Margins of inline-block boxes do not collapse (not even with their in-flow children).
Therefore you could also add change the display of the elements to inline-block:
.collapsing-margins {
margin: 100px 0;
background: #f00;
display: inline-block;
width: 100%;
}
<div class="parent">
<div class="collapsing-margins">Element</div>
<div class="collapsing-margins">Element</div>
</div>

Margin collapse on inline-block elements?

Margins of blocks elements collapse, but not inline-blocks.
Is there a way to force inline-blocks margins to collapse?
.wrapper {
position: relative;
float: left;
width: 100px;
margin: 10px;
}
.wrapper .el {
display: inline-block;
width: 100%;
height: 20px;
background: #000;
margin: 10px 0;
}
.wrapper.block .el { display: block; }
<div class="wrapper">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
<div class="wrapper block">
<div class="el"></div>
<div class="el"></div>
<div class="el"></div>
</div>
Anyone have an idea?
I have already read the documentation on MDN.
This is documented in the spec that margins of inline-block elements do not collapse:
8.3.1 Collapsing margins
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).
...
Therefore the answer is No. You probably need to alter the margins of the element.
The answer is "no" because that's not how inline boxes work so it can't be forced as you asked for. Anything else would be just manipulating the margins of elements which is only a trick or hack.
If I get you right you want to remove that extra margin that inline-block elements have assign font-size: 0; to the parent element of the corresponding div.
Check this post:
http://css-tricks.com/fighting-the-space-between-inline-block-elements/

Is it possible to collapse through left and right margins with a parent element?

I'd like to collapse the left and right margins of a block through its parent.
I cannot change the style of the child, because its margin is unknown. It can be anything. I want the parent's background to not show in the area of the child's margins. Vertical margins behave this way, and I want to find a way to make horizontal margins behave this way.
For example (I'd like the red not to be visible at all):
.a {
background: red;
margin: 10px;
}
.b {
margin: 10px;
height: 100px;
background: blue;
}
body {
background: yellow;
}
<div class="a">
<div class="b">
x
</div>
</div>
According to the docs, left/right margin collapse through only happens if block-progression is lr or rl (which isn't really a thing browsers support it seems).
http://www.w3.org/TR/css3-box/#collapsing-margins
Is there another way?

Resources