Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Matthew Reinbold
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Matthew Reinbold@libel_vox )

Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit

By Ben Nadel on
Tags: HTML / CSS

Over the weekend, I ran into an interesting behavior in Webkit (Chrome and Safari) in which a Body tag with 100vh (100 vertical-height units) was causing a vertical scrollbar to appear despite the fact that the dimensions of the Body tag seemed to be constrained to the viewport. After slowly deleting a lot of code, I finally discovered that the scrolling behavior was the result of an unexpected margin collapse off the bottom of the HTML document. And, once I removed the margin collapse, the scrollbar disappeared.

To see this in action, consider the following HTML document:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit
  • </title>
  •  
  • <style type="text/css">
  •  
  • html,
  • body {
  • margin: 0px 0px 0px 0px ;
  • min-height: 100vh ;
  • padding: 0px 0px 0px 0px ;
  • }
  •  
  • h1 {
  • margin-top: 0px ;
  • }
  •  
  • </style>
  • </head>
  • <body>
  •  
  • <h1>
  • Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit
  • </h1>
  •  
  • <p>
  • Hello, this is some copy.
  • </p>
  •  
  • </body>
  • </html>

As you can see, I'm using 100vh on the Body tag. And, I'm removing any margin or padding on the body to make sure the box-sizing doesn't throw us off. Now, if I open this in the browser and adjust my viewport to be 225px tall, we get the following output:


 
 
 

 
 Margin collapsing causing unexpected scrollbars with 100vh body in Webkit. 
 
 
 

As you can see, the Body is reporting as 225px tall - the same as the viewport; but, we're still getting a vertical scrollbar.

The problem turns out to be the P tag inside the body. By default, the P tag has a top and bottom margin. And, if we mouseover the P tag in the element viewer, we can see this margin being rendered:


 
 
 

 
 Margin collapsing causing unexpected scrollbars with 100vh body in Webkit. 
 
 
 

As you can see, the P tag has about 16px of margin in my browser. What you can also clearly see is that the P tag is fully contained within the bounds of the Body tag. In fact, there's over 100px of white-space between the P tag and the bottom of the viewport. The bottom-margin of the P tag is, however, still - I think - "collapsing" outside of the Body tag, causing the unexpected scrollbar.

To work around this bizarre margin collapsing, we can set the bottom-margin of the P tag to be zero:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit
  • </title>
  •  
  • <style type="text/css">
  •  
  • html,
  • body {
  • margin: 0px 0px 0px 0px ;
  • min-height: 100vh ;
  • padding: 0px 0px 0px 0px ;
  • }
  •  
  • h1 {
  • margin-top: 0px ;
  • }
  •  
  • p {
  • margin-bottom: 0px ; /* To work-around bizarre margin-collapsing. */
  • }
  •  
  • </style>
  • </head>
  • <body>
  •  
  • <h1>
  • Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit
  • </h1>
  •  
  • <p>
  • Hello, this is some copy.
  • </p>
  •  
  • </body>
  • </html>

Now, when we render the same page, we get the following output:


 
 
 

 
 Margin collapsing causing unexpected scrollbars with 100vh body in Webkit. Removing the content margin can help. 
 
 
 

As you can see, despite the fact that the P tag is nowhere near the bottom of the page, removing the margin-bottom from the P tag ends up removing the unexpected scrollbar.

Now that we know the problem relates to margin-collapsing, we can think of other solutions - this isn't the only work-around for this problem. I could have also added a 1px padding to the bottom of the Body tag (while setting the "box-sizing" to "border-box"). This 1px of padding would prevent the P tag margin from collapsing across the Body boundary. I could have also added another element between The P tag and the Body (such as a Div). This additional element would have prevented the P tag margin from collapsing across the Body boundary.

I could have also added an ::after pseudo-element on the Body tag to prevent the margin collapsing:

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit
  • </title>
  •  
  • <style type="text/css">
  •  
  • html,
  • body {
  • margin: 0px 0px 0px 0px ;
  • min-height: 100vh ;
  • padding: 0px 0px 0px 0px ;
  • }
  •  
  • /*
  • This element prevents the page content from margin-collapsing across the
  • bottom boundary of the Body box.
  • */
  • body::after {
  • content: "" ;
  • display: table ; /* NOTE: Display "block" does not seem to work with height: 0px. */
  • height: 0px ;
  • }
  •  
  • h1 {
  • margin-top: 0px ;
  • }
  •  
  • </style>
  • </head>
  • <body>
  •  
  • <h1>
  • Margin Collapsing Causes Unexpected Scrollbar With 100vh Body In Webkit
  • </h1>
  •  
  • <p>
  • Hello, this is some copy.
  • </p>
  •  
  • </body>
  • </html>

In this case, I'm creating an empty pseudo-element with no height-dimension (which only seems to work with display "table", not display "block"). This zero-height element is sufficient for preventing the margin collapsing and is able to remove the unexpected scrollbar in Webkit without taking up any visual real estate. I think this is the most practical of the approaches that I can come up with.

Anyway, hopefully this can help someone else who comes across this mostly unexpected margin collapsing behavior in Webkit.



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

It only seems to happen when you apply min-height: 100vh; to both html and body.

If you instead only apply it to the body the problem goes away.

https://plnkr.co/edit/wIYU48?p=preview

Reply to this Comment

By setting html { height: 100vh; } and body { min-height: 100vh; } it works better.

https://plnkr.co/edit/wIYU48?p=preview

Reply to this Comment

This is standard question on HTML/CSS/JS job technical interview, its a part of the CSS 2.1 specs and is considered to be known by experienced devs (i.e. we check if the years experience are indeed an actual experience - if one has encountered it most probably they did dev for so much time).

In here (https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing) you have the second type - child collapsing its margins with its parent and ending up outside the parent.

Reply to this Comment

@Peter,

Right - it's not the margin-collapsing in general that is surprising me. It's the fact that it happens even when the edge of the child element is not near the edge of the parent element. In this case, the bottom of the P tag can be hundreds of pixels away from the bottom of the Body tag. And still, the margin-collapsing takes place.

It feels very much like the "letter" of the specification and the "intent" of the specification might be at odds in this case?

And, just to circle back to the browser inconsistency, if Firefox doesn't show the margin-collapsing in this case, would it be fair to say that Firefox is not adhering to the specification?

Reply to this Comment

@Šime,

Very cool! Sounds like they are saying Chrome is right, Firefox is wrong ... but, that feels wrong in my heart :D

Reply to this Comment

@PeterStJ,

I think you did not read the article.

The author knows perfectly well what margin collapsing is. He is wondering why does it happen in this particular situation. Please read the article before criticizing and talking about how basic of a knowledge this is.

Reply to this Comment

@Greg,

I did nothing of the sorts (of criticizing) I just stated that we (at the company I was at the time) used this question often to test if the candidate have encountered it and on top of other questions attempted to determine if the developer is used to raw css or is mainly using css frameworks. I also linked the resources we give back to candidates for ALL questions as a standard practice, regardless of their level. I do not see anything wrong with that either.

Maybe you misread my comment? Anyway thank you for letting me know the possibility to comprehend the wording as critique - English is not my native language and I completely understand that often I might be using wrong words.

Reply to this Comment

@PeterStJ,

Hey Peter,
I knew what you meant - all I wanted to say is that the topic discussed in this article is not a classical example of margin collapsing, the one we all know and about which we can all read in various blog posts and on MDN.

Anyway, sorry if I sounded a bit off too.

Have a great day!

Reply to this Comment

@Peter, @Greg,

While I do love me some margin-collapsing, I have to admit, that I do find myself using this kind of CSS in my apps:

.some-container p:first-child {
    margin-top: 0px ;
}
.some-container p:last-child {
    margin-bottom: 0px ;
}

While I love margin-collapsing in the flow of content, I often find it's not exactly what I want at the boundaries of a layout container. Of course, this is not always true. With a simple layout, the margins can "just work". But, there are definitely cases where it feels like they get in the way.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.