Does CSS grid suppress margin collapse inside grid items? - css

I understand that a CSS grid element suppresses margin collapse between child grid items that are adjacent (adjacent siblings).
But what I think I'm seeing is that a grid element also seems to suppress margin collapse within a single grid item, by suppressing margin collapse between that item and that item's own children (parent-descendant margin collapse).
Is that what's going on? Is that what's supposed to happen?
You can see it in a snippet like this:
main {
display: grid;
outline: 1px solid green;
}
section {
margin: 25px 0 25px 0;
outline: 1px solid red;
}
div {
margin: 25px 0 25px 0;
outline: 1px solid blue;
}
<main>
<section>
<div>X</div>
<div>Y</div>
</section>
</main>
As you can see if you run this snippet, there is 25 px of space between X and Y. This is because the X's 25px margin-bottom is collapsing with its adjacent sibling Y's 25px margin-top, as expected.
But there is 50 px of space between the X content area (outlined in blue) and the top of the grid item content area (outlined in green). This seems to be because the X div's 25px margin-top is adding and not collapsing with the section's 25px margin-top. This is what I find confusing.
If you switch main to display:block, then you get the normal collapse behavior, where the margin-top of div and of section collapse together. They also collapse with the margin-top of main itself, so that marginal space extends outside main's content area (outlined in green).
I don't understand why this is happening and can't see what in the spec would imply that a grid should affect collapse behavior within a grid item.

From the specificaton:
A grid item establishes an independent formatting context for its contents. However, grid items are grid-level boxes, not block-level boxes: they participate in their container’s grid formatting context, not in a block formatting context.
and
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.ref
So margin inside a grid item cannot collapse with the margin of the grid item that are outside
You can have margin collapsing inside the grid if all the concerned margin are inside
main {
display: grid;
}
section {
margin: 25px 0;
outline: 1px solid red;
}
div {
margin: 25px 0;
outline: 1px solid blue;
}
<main>
<section>
<div>X</div>
<div>X</div>
</section>
</main>
As you can see, we only have 25px between the two divs inside the grid item

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.

Why is bottom margin inside element box for button but not p element?

Why is it that adding a bottom margin for a p element adds space beyond the element's box (that is not affected by the background color set in CSS), but the inverse is true (the margin remains colored) when adding a bottom margin for a button? (I fixed this by using padding only and setting margin to 0 for the p element, so I'm only asking out of curiosity.)
This CSS:
.about p {
padding: 0 4em 2em 4em;
margin: 0 0 2em 0;
text-align: left;
}
button {
margin-top: 1em;
margin-bottom: 2em;
}
Resulted in (note the white space above the "PORTFOLIO" section but not below the button at the bottom):
To view the rest of my code, view CodePen.
I can't seem to find any suitable question to close this as a duplicate of, so here's an answer.
You are the victim of margin collapsing in the case of the p.
See this example. The outer element has a yellow background, and you would expect to see this behind the p (above and below), but it "inherits" the p's margin, so what you're seeing is the white background of the body.
.outer {
background: yellow;
}
.inner {
background: lime;
margin: 2em 0;
}
<div class="outer">
<p class="inner">two block elements</p>
</div>
The button in your example, however, is not a block element. It has display:inline-block by default, so no margin collapsing takes place and the outer div's background is visible around it.
.outer {
background: yellow;
}
.inner {
background: lime;
margin: 2em 0;
}
<div class="outer">
<button class="inner">inline-block in a block</button>
</div>
For more information, see Mastering margin collapsing on MDN.

how to make a text ele to the right of a left floated ele automatically occupy the rest width?

<div style="width:<?php echo $w; ?>">
<div style="float:left;margin-right:10px;width:200px">whatever</div>
<h2 style="border-bottom:solid 1px black">title</h2>
</div>
By default, the width of h2 would be $w, and the bottom border will strike the inner div, I want the h2 to automatically occupy the rest width, no matter what $w's value is, the h2's width should be $w - 200 - 10. How to achieve this in pure CSS?
PS: if I set css h2{float:left}, h2's width would depend on its inner text's length. To have the h2 element occupy the rest of the width (so that I can have a beautiful bottom border) I have to manually set a width value for h2, which is what I'm trying to avoid.
From the spec,
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 (such as an element with 'overflow' other than 'visible') must
not overlap the margin box of any floats in the same block formatting
context as the element itself.
Therefore, you can just add
h2 {
overflow: hidden;
}
div {
width: 500px; /* $w */
}
div > div {
float: left;
margin-right: 10px;
width: 200px;
}
h2 {
border-bottom: solid 1px black;
overflow: hidden;
}
<div>
<div>whatever</div>
<h2>title</h2>
</div>
You may also want to style the outer div with overflow: hidden just in case the floated element is taller than the h2.

Margin collapse issue between div

I'm expecting the vertical gap between bottom border of first div and top border of second div to be 45px in this case but browser collapse bottom and top margin.
Effective gap b/w bottom border of first div and top border of second div in browser display is 25px.
Ideally border should prevent margin collapse. What is the mistake here?
I have not applied any positioning or float.
jsfiddle Code
HTML
<body>
<div><p>A</p></div>
<div><p>B</p></div>
</body>
CSS
div{
width:200px;
height:200px;
}
div:nth-child(1){
background-color: #F52C6F;
border-bottom: 10px solid black;
margin-bottom: 20px;
}
div:nth-child(2){
background-color: #0ECCCC;
border-top: 10px solid yellow;
margin-top: 25px;
}
Ideally border should prevent margin collapse. What is the mistake here? I have not applied any positioning or float.
Borders do not block margin collapse between siblings — they only block margin collapse between a parent and a child, or wherever the borders would otherwise intersect the margins.
From the spec:
Two margins are adjoining if and only if:
...
no line boxes, no clearance, no padding and no border separate them
...
Since the borders aren't actually separating or intersecting the margins between your two div elements, the margins are considered adjoining, and therefore margin collapse occurs between them as usual. The borders on your div elements would however prevent margins of your divs collapsing with those of their p children.
This behaviour is actually documented in the W3C Specification on the box model: Collapsing Margins.
Basically, adjoining vertical margins collapse into one margin, where the larger margin value is used and the lower value is discarded.
Use one higher margin value instead of two lower. :-)
Try somthing like this:
Html:
<body>
<div id="parent">
<div><p>A</p></div>
<div><p>B</p></div>
</div>
</body>
CSS:
#parent
{
width:200px;
height:200px;
}
#parent div:nth-child(1){
background-color: blue;
border-bottom: 10px solid black;
margin-bottom: 20px;
}
#parent div:nth-child(2){
background-color: green;
border-top: 10px solid yellow;
}
And here is a working jsFiddle: http://jsfiddle.net/hEDR3/

How does the CSS Block Formatting Context work?

How does the CSS Block Formatting Context work?
CSS2.1 specifications says that in a block formatting context, boxes are laid out vertically, starting at the top. This happens even if there are floated elements in the way, except if the block box established a new block formatting context. As we know, when browsers render block boxes in a block formatting context, the floated element is omitted, why does establishing a new block formatting context work?
How are boxes (block boxes and inline boxes) laid out in the normal flow?
I read somewhere that block elements generate block boxes, but floating elements are ignored when a user agent draws box and take them into account when they fill out content. Whilst floating elements will overlap other elements's boundary of the boxes, the solution is establishing a new block formatting context for the overlapped elements using overflow:hidden.
"New block formatting context is still block formatting", so when drawing a box, it will also treat the floating element as if it doesn't exit. Is that right or have I misunderstood "new block formatting context?"
Update:more questions
By saying "It's this behaviour that's useful for columnar style layouts. The main use of it however is to stop floats, say in a "main content" div, actually clearing floated side columns, i.e. floats that appear earlier in the source code."
I don't understand the meaning, I will provide an example, maybe you will understand me.
.content {
background: #eee;
color #000;
border: 3px solid #444;
width: 500px;
height: 200px;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: right;
}
p {
background: #444;
color: #fff;
}
<div class="content">
<h3>This is a content box</h3>
<p>It contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float. This is normal behaviour.</p>
<div class="float">floated box</div>
</div>
I thought the floating box should float to the top of the containg block-the div with class content. Besides, if the floating box appears earlier in the markup, then it will display what I think it should be.
.content {
background: #eee;
color #000;
border: 3px solid #444;
width: 500px;
height: 200px;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: right;
}
p {
background: #444;
color: #fff;
}
<div class="content">
<div class="float">floated box</div>
<h3>This is a content box</h3>
<p>it contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float, this is normal behaviour</p>
</div>
How can we explain this? Can we use "block formatting context and inline formatting context" to explain it?
Block Formatting Contexts
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.
With my bold, it's the establish bit that is important
What this means is that the element you use overflow (anything other than visible) or float or inline-block..etc on becomes responsible for the layout of its child elements. It's the child elements which are then "contained", whether that's floats or collapsing margins they should be wholly contained by their bounding parent.
In a block formatting context, each
box's left outer edge touches the left
edge of the containing block (for
right-to-left formatting, right edges
touch)
What the above line means:
Because a box can only be rectangular and not irregular shaped this means a new block formatting context between two floats (or even beside one) will not wrap around neighbouring side floats. The inner, child boxes can only extend as far as to touch their parents left (or right in RTL) edge. It's this behaviour that's useful for columnar style layouts. The main use of it however is to stop floats, say in a "main content" div, actually clearing floated side columns, i.e. floats that appear earlier in the source code.
Float Clearance
In normal circumstances floats are supposed to clear all previous floated elements, that's previously floated in the whole source code, not just your displayed "column"
The telling quote from the "float clearance specs" is:
This property indicates which sides of
an element's box(es) may not be
adjacent to an earlier floating box.
The 'clear' property does not consider
floats inside the element itself or in other block formatting contexts
So say you have a three column layout where the left and right columns are floated left and right respectively, the side columns are now in new Block Formatting Contexts, because they are floated (remember float is also one of the properties that establish a new BFC), so you can happily float list elements inside them and they only clear floats which are already inside the side columns they no longer care about floats previously in the source code
##To make the main content a new Block Formatting Context or not?
Now that middle column, you can simply margin it from both sides so that it appears to sit neatly between the two side floated columns and take the remaining width, a common way to get desired width if the centre column is "fluid" - which will be fine until you need to use floats/clearance inside your content div (a common occurrence for those using "clearfix" hacks or templates including them)
Take this very simple code:
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
margin: 0 200px;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated">this a floated box</div>
</div>
It produces the following:
In general this is fine, especially if you have no background colours or internal (in the main content) floats - notice the floats are fine (not cleared yet) they're doing probably what you except them to but they, the H3's top margin and the p's bottom margin are not actually really contained by the content div (lightgrey background).
So to the same simple margined scenario of above code add:
.clear-r {clear: right;}
to the CSS, and change the second HTML floated box to:
<div class="floated clear-r"> this a floated cleared box</div>
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
margin: 0 200px;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
.clear-r {
clear: right;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated clear-r">this a floated cleared box</div>
</div>
This time you get this:
The second float is clearing the right side but it's clearing the whole height of the right column. The right column is floated earlier in the source code so it's clearing it as told! Probably not the desired effect though, also note the h3 and p margins are still collapsed (not contained).
###Make it establish a Block Formatting Context, for the sake of the children!
and finally make the main content column take responsibility - become a Block Formatting Context - for its contents : remove margin: 0 200px; from the main content CSS and ADD overflow: hidden; and you get this:
#left-col {
border: 1px solid #000;
width: 180px;
float: left;
}
#right-col {
border: 1px solid #000;
width: 180px;
float: right;
height: 200px;
}
#content {
background: #eee;
overflow: hidden;
}
.floated {
float: right;
width: 180px;
height: 100px;
background: #dad;
}
.clear-r {
clear: right;
}
<div id="left-col">left column</div>
<div id="right-col">right column</div>
<div id="content">
<h3>Main Content</h3>
<p>Lorem ipsum etc..</p>
<div class="floated">this a floated box</div>
<div class="floated clear-r">this a floated cleared box</div>
</div>
This is probably much more like what you would expect to happen, note now the floats are contained, they clear properly ignoring the right side column, and also the h3 and p margins are contained instead of collapsed.
With the extensive use of resets these days the margins are less noticeable (and IE still doesn't get them quite right) however what just happened to the centre "main content" is that it became a Block Formatting Context and is now responsible for its own child (descendant) elements. It's actually very similar to Microsoft's early days notion of hasLayout, it uses the same properties display: inline-block, float, and overflow anything other than visible, and of course table cells always have layout.. it is however without the bugs ;)
##Update: re more information in question:
When you say "but floating elements are ignored when user agent draws box and take them into account when they fill out content."
Yes floats normally overlay their container boxes, is that what you mean about parent boundaries? When a block element is drawn and it contains a float the block parent itself is drawn as a rectangle under the float and it is the "inline anonymous boxes" or simply "line boxes" of the other child elements that are shortened to make room for the float
Take this code:
#content {
background: #eee;
color #000;
border: 3px solid #444;
}
.float {
background: rgba(0, 0, 255, 0.5);
border: 1px solid #00f;
width: 150px;
height: 150px;
float: left;
margin: 10px;
}
p {
background: #444;
color: #fff;
}
<div id="content">
<div class="float">floated box</div>
<h3>This is a content box</h3>
<p>it contains a left floated box, you can see the actual content div does go under the float, but that it is the <h3> and <p> <b>line boxes</b> that are shortened to make room for the float, this is normal behaviour</p>
</div>
Which produces this:
You see that the parent element doesn't actually contain the float, as in it doesn't wrap it entirely.. the float is simply floating on top of the content - if you were to keep adding content to the div it would eventually wrap underneath the float because there would be no need for the (anonymous) "line boxes" of the p element to shorten themselves any more.
I've coloured the paragraph element so you can see that it too actually goes under the float, the darkgray background is where the paragraph starts, the white text is where the "anonymous line box" starts - it's only actually them that "make room" for the float, unless you tell it otherwise (i.e. you change the context)
Again referring to the above picture, if you were to margin the left side of thep element, yes it will stop the text wrapping under the bottom of the float because the "line boxes" (the white text) will only touch the left edge of their container, and you will bring the coloured background of the p to the right, clear of the float, but you won't have changed the behaviour of the p's formatting context. Like the centre column in the first picture way above ;)

Resources