I was working on this CSS, I have wrapped an article inside a div. I have given percentage width for both, and padding for the div. My padding seem "not applied" until I add border to the article. You can see the difference by commenting the border applied to the article. My article height shrink. Why is this happening?
<style>
body{
margin:100px;
}
.content{
width:100%;
padding:.9746589%;
background:green;
}
.content>article{
width:100%;
background:yellow;
border:1px solid red;
}
</style>
<div class="content" role="main">
<article>
<p>This is my text!</p>
</article>
</div>
There are default margins in your p element that are collapsing with your article element. When you add a border to your article, it prevents that collapse from happening and causes it to contain the p element and its default margins.
Your .9746589% padding is applied in both situations, but since you put it on .content it isn't actually affected by the margin collapse between its article and the p that's inside it. If you were to remove that as well as the border, though, then the margins would collapse across all elements and the green background would completely disappear. In other words, just like how your border is blocking margin collapse between article and p, your padding is also blocking margin collapse between .content and article and its contents.
Lastly, margins only collapse vertically, so setting width will never make a difference.
Related
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>
The definition of overflow:hidden states that :
the overflowing content is completely hidden, not accessible to the user.
from: http://quirksmode.org/css/css2/overflow.html
But i am curious about this behavior from my js fiddle:
https://jsfiddle.net/gd62qmr3/2/
One thing i noticed is that, it gave color to the margins after setting the overflow to hidden?
<div style="background-color:green;overflow:hidden;">
<p>test</p>
</div>
I wanna know why did such behavior occur? the exact block element will have the green background color if there is no overflow but putting overflow will give background color to its margins.
That's because overflow: hidden affects margin collapse.
p elements have some vertical margin by default. According to the spec, it collapses with the margin of the parent div:
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.
Adjoining vertical margins collapse, except [... not
relevant]
[... The] top margin of a box and top margin of its first in-flow
child [are adjoining]
However, overflow: hidden prevents that:
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.
Your color is set to the parent of your paragraph. You have nothing stopping the height of your parent div to expand as far as it wants, so the paragraph margins are making the parent height larger.
If you were to give a set height to your parent (18px or so) then it will hide the margins (and text technically)
div {
height:18px;
}
https://jsfiddle.net/gd62qmr3/3/
If you were to set the color to your paragraph, then you will not see the background color in the margins
div {
overflow:hidden;
}
div p {
background-color:green;
margin:20px 0;
}
https://jsfiddle.net/gd62qmr3/4/
Having a few elements with height:100vh, they show a vertical spacing when containing elements.
<section>
<p>A</p>
</section>
<section>
<p>B</p>
</section>
With the styling:
section {
height:100vh;
}
You can se an example here, the yellow background is visible above each element, even though the margin of the sections is set to 0.
How can the spacing be removed?
Just add overflow:auto; to .content>section
.content>section {
height: 100vh;
margin: 0;
overflow:auto;
}
alternatively you can remove the margin from all elements but then you have to edit both margin and padding to the element which may need it.
* {
margin:0;
padding:0;
}
Each browser will have a default user agent style sheet that will add some margin to all h1 elements for example, so you can remove it overriding the user agent style sheet rules for the elements you need.
h1 {
margin:0;
}
You can avoid these kind of issues by using a reset.css file to reset all different browsers default margin and padding for instance.
It's because h1 and p tags have default margin (or margin collapsing).
Margin collapsing
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. by Mozilla MDN
JSFiddle - DEMO
You could do like this to remove the margin:
CSS:
h1, p {
margin:0;
}
Here is working example. Please change html,body with * in css rule.
* {
margin:0;
padding:0;
}
There is a block:
<div class="block">
<p>this is a paragraph this is a paragraph</p>
</div>
And its style:
.block{background:gray; width:300px}
p{margin-top:50px}
This is the result:
Well, but when I add padding-top to the .block, the result differs:
.block{background: gray; width:300px; padding-top:1px}
p{margin-top:50px;}
The result:
It looks that by adding padding to the parent, the margin of the child (p) caused expanding.
You could checkout the fiddles: http://jsfiddle.net/YFU2f/2/ and http://jsfiddle.net/YFU2f/3/
By default, adjacent margins collapse, when you introduce a padding between them they aren't anymore adjacent, so they become separate.
In your example, the top margin you defined on the p element collapses with the top margin of the body element (which is usually 8px by default), so at the end, the distance between the top of the canvas and the top of the content box of p is 50px. If you introduce the padding, it becomes 8px+1px+50px=59px. You can also notice the collapse of the margin by looking at the background of the div: when the margin collapses, it is as if the 50px margin was only declared for the body.
You can find all the details in the specifications: http://www.w3.org/TR/CSS21/box.html#collapsing-margins
you need to use display p as block
p{margin-top:50px;display:inline-block;}
I'm a fairly novice web developer and I'm having a very fundamental problem that I would really appreciate some help with:
No matter what width I set any elements within a certain containing div, safari and Chrome both add extra margins that fill the width of the div. If I specify them to have 0 margins the css is overridden. For example, I have the following
<div class="container">
<div class="element1">
...
</div>
</div>
and I set this as the css:
.container{
background-color:#ffffff;
margin-left:7.5%;
margin-right:7.5%;
padding:30px;
color:#505050;
line-height:1.5;
font-size:14px;
}
.element1{
max-width:50%;
margin: 0px 0px 0px 0px;
}
element1 has a width of 50% of the containing element, but it then has an extra margin to the right that fills up the rest of the width of the containing element. Why is this happening and how do I set this right-margin to 0?
Thanks!
Try adding in a reset stylesheet before your stylesheet to normalise all the browsers. Browsers have their own ideas about default padding and margins etc. for different elements. By resetting the stylesheet, you are making every browser start from the same position.
http://meyerweb.com/eric/tools/css/reset/
It seems to me that you don't understand the concept of block level elements.
"By default, block-level elements are formatted differently than inline elements. Generally, block-level elements begin on new lines, inline elements do not. For information about white space, line breaks, and block formatting, please consult the section on text."
http://www.w3.org/TR/html4/struct/global.html#h-7.5.3