No Margin for Error | November 23, 2003

Margin Collapsing

Margin collapsing is a CSS phenomenon I've been familiar with for a while. Conceptially It's very simple, however recently I've been running into a number of problems with my layouts where margin collapsing appears to be the culprit.The problems usually revolve around either extra vertical white space appearing that I just can't seem to remove or the inability to create vertical white space by using margins.

At it's core, margin collapsing is very easy to understand. Basically when two vertical margins meet up, instead of adding together, the largest margin takes precedent and the other one "collapses" to nothing.

Here is a simple example:

This paragraph has a 20px margin

So does this one

Both paragraph tags have been given a 20 pixel margin. However because the bottom margin of the first paragraph touches the top margin of the second paragraph, the margins collapse, making the space between both paragraphs 20 pixels instead of 40.

Most people think of margin collapsing happening when one block level element follows another. However margins collapse whenever one margin comes into contact with an adjacent margin. This means that margins can also collapse when one element is contained within another.

A paragraph with a 20px margin inside a div, also with a 20px margin

In the above example a paragraph (light blue) with a 20 pixel margin is wrapped inside a div (medium gray), also with a 20 pixel margin. The top and bottom margins of the paragraph tag collapse into the margins of the div tag leaving just a 20px vertical margin around both elements.

Now you've probably noticed that these two tags are contained within another div that I've applied a border to. Most people will assume i've done this just to make the examples stand out. However I've actually done this for another reason.

Here is the same example with the border on the outer div removed.

By removing the border around the outer div, the margins of the contained elements are now adjacent to the margin of the preceding element (in this case a paragraph tag) and have all collapsed together. Now while this is what margin collapsing is supposed to do, if you don't know what's going on here, this kind of thing can be a bit of a head scratcher.

There are number of ways to get round margin collapsing issues. One way is to add a border or 1px of padding around the elements so that the borders are no longer touching and so no longer collapse.

The same example but this time each element has been given a border

Another way to stop margins collapsing is to change the position property of the element.The CSS2 Specs explain that margins of absolutely and relatively positioned boxes don't collapse. Also if you float a box it's margins no longer collapse. It's not always appropriate to change the position properties of an element but in some situations if you're having problems with unwanted margin collapsing, this may be an option.

Before I move on, it's probably worth bringing up self collapsing boxes. A self collapsing box is one where it's top and bottom margins meet.

Here is a very simple example of a self collapsing block

This paragraph has a 20px margin. After this paragraph is another, empty paragraph, also with a 20px margin

Because the second paragraph has no content, it's margins collapse together and they in turn collapse with the preceding paragraph. This is why you can put loads of empty paragraph tags in a page yet they take up no space. Usually this won't cause any problems, however people occasionally use empty elements to clear a float for instance, so this behavior needs to be born in mind.

Cleared Elements

On the subject of margins and floats, here is an interesting situation I came across a few days ago while trying to lay out a page.

This div is floated left
This div is cleared and has a 20px top margin

In the above example it would be reasonable to expect that the div with the 20px margin would be 20 pixels below the floated div. However in actual fact it appears to be only a couple of pixels below the floated div.

If you look at the CSS2 Specs for clearing floats you'll get a better understanding of what's going on. When you clear something, what actually happens is the element you've applied the clear to increases it's margin enough so that it clears the proceeding floated boxes. If that element already has a margin the largest margin wins. In this instance the 20px margin is just slightly larger than the height of the floated box and it's top starts at the top of the floated div. Because the floated div is slightly less than 20px tall you get a small gap between the two elements background colours.

In a layout I was working on recently I ended up with both these phenomenon happening at once. Things were collapsing all over the place (literally and figuratively). I even ended up having problems with an empty div I was using to clear a float apparently collapsing in on it's self and then collapsing with other elements, causing all sorts of layout problems. So if you find yourself in a situation where you are using margins to space out elements but are getting unpredictable results, it may be down to margin collapsing, it may be down to cleared elements and if you're really unlucky it may be down to both.

Posted at November 23, 2003 10:24 PM

Comments

Eric TF Bat said on November 23, 2003 11:21 PM

In IE6/Win, the last example does, indeed, have the margin you expected. In Moz 1.5/Win it doesn’t, in keeping with the spec. I’m seeing more and more that it’s essential to start one’s testing in the most compliant browser available, ie the latest version of Mozilla. After that, you test with the most popular browsers, ie IE5+/Win. If one must produce workarounds for browser bugs, it’s a better thing to start with correct code and selectively add the least possible hackery, rather than to start with a mess of hacks and twiddles and then have to figure out which ones you can safely remove!

ste said on November 23, 2003 11:31 PM

Now what I don’t get is why IE and Mozilla differ on whether to collapse margins on elements at the beginning and end of an enclosing block element. For example, an h1 (say with a margin-top of 15px) at the beginning of a div will have 15px of space above in Mozilla but not in IE. I’ve noticed the same behavior at the end of an enclosing div as well. Which behavior is correct? (I’m guessing Mozilla’s just because that generally has better standards compliance.) Personally, I prefer the margins collapsing, but that may just be because I’m so used to it in IE…

Scrivs said on November 24, 2003 5:17 AM

Andy, you have no idea how many issues this solves for me. I have experiencing this phenom for months and always came up with crazy workarounds that I didn’t understand worked. Thank you so much for this writeup, couldn’t have come at a better time. Also adding it to the vault.

Tim said on November 24, 2003 9:09 AM

“In IE6/Win, the last example does, indeed, have the margin you expected.”

Eric beat me to it.
Same goes for Opera 7.2.

Andy Budd said on November 24, 2003 9:55 AM

Browser discrepancies throw an extra level of complication into the mix. My guess is that Mozilla and Safari are actually getting it right, but IE conforms to what people expect to happen. Thus you get into a situation where you think one browsers is buggy when actually it’s another.

These kind of issues can prove to be real head scratchers.

Andy Smith said on November 24, 2003 12:36 PM

In the last example I agree that Mozilla is right - giving an element a ‘clear’ property means the element’s top border edge has to be below the bottom margin edges of any floating elements, but the top margin shouldn’t be increased any more than is necessary for that.

If you want a gap between floating and cleared elements, I think you’d get more consistency if you did it as a bottom margin on the floating element rather than a top margin on the cleared element. You could also use padding on the cleared element if you don’t mind its border and background going all the way up to the bottom to the floating element.

Andy Budd said on November 24, 2003 1:54 PM

Here’s a little problem I’ve been struggling with.

http://www.message.uk.com/test/float-test.html

If anybody can shed some light on this one there’s a pint in it for you, next time you visit Brighton :-)

Jason said on November 24, 2003 3:26 PM

Even more odd: Remove the clear: both; (or change in to clear: none; or even clear: right;) rule from #panel and the extra space from the h1’s bottom margin seems to be added to the padding of the #panel div. This is in IE5.5/Win.

At first I thought IE5.5 is calculating the amount that #panel has to have added to its margin to clear it past #nav incorrectly, somehow involving the margin on h1. But then when you set #panel to display: none the white space is still there.

Then I realized the margin could be getting added to the hr element.

So I removed every element below #panel and changed #panel to display: none;

The result is no extra margin being added to the bottom of #nav (Shrink the borwser window so the bottom of the window is near the bottom of #nav and there is no vertical scroll bar.) Add margin bottom to #nav and the scrollbar returns.

Put everything back:
Now, hide #panel and put clear: both on the hr element and the whitespace vanishes.

Remove the hr and put clear: both on h2 and the space is back?

Andy Budd said on November 24, 2003 5:11 PM

It’s a poser isn’t it. I’ve been playing around with various variations all day and I still can’t make out what’s going on.

Andy Budd said on November 24, 2003 5:14 PM

It’s a poser isn’t it. I’ve been playing around with various variations all day and I still can’t make out what’s going on.

Josh said on November 24, 2003 5:45 PM

I have a headache…

Tim said on November 24, 2003 6:10 PM

What’s causing me headaches is this:
http://www.timspelt.nl/andy/

Gabriel Mihalache said on November 24, 2003 6:49 PM

Is there a place where I can find tips about using this with tables?

Michael said on November 24, 2003 7:24 PM

Yeah, this kind of thing seems to give headaches to the people at the other end, too:

http://weblogs.mozillazine.org/hyatt/archives/2003_08.html#003945

I hope you’ll be giving the winner, if there is one, some local beer, Andy. :-)

http://www.harveys.org.uk/t1seasonaldraughta.htm

Adam said on November 24, 2003 7:56 PM

I can’t really explain it, but applying a float:left to h1 removes the gap in IE6. However, it slightly increases the vertical space above the same tag in Mozilla. Just thought I’d throw it out there as perhaps a step in another direction.

Tom Clancy said on November 24, 2003 8:56 PM

Echoing, Scrivs above, I’d like to thank you for letting me know someone else is suffering from this malady as well. Not sure what’s going on in your example, but it reminded me of this: http://www.positioniseverything.net/articles/float-theory.html

Jason said on November 24, 2003 9:16 PM

More Wierdness:

Ok so I was playing with this a little more and I am still not sure what exactly Internet Explorer is doing. But here is some new wierdness I have encountered.

I was playing with negative margins on various elements to try and shed some more light on what is happening here. Well with larger values for negtive margins on things like the h1 move some elements right off the top of the window, so it is hard to see what is going on, when you can’t see it. So I decided to make a container to put all of this in and then try negative marginsso I could see how far above the container stuff was being placed.

So I clean everything below #panel off of the page and put the container div around the remaining elements.

I put a 40px margin and 5px padding on the container and a 1px border.

Well in Mozilla nothing major changes other than the size of the objects in relation to the browser window size. But in IE5.5 I get some bizarre stuff.

The width: 100%; on #nav makes that div stretch too far because it sizes relative to the browser window not the containing div.

But what is really wierd is that when width is 100% I get TWO bottom borders for the container div. One border is flush with the bottom of #nav and the other is 5px off of the bottom of #panel.
The side borders extend all the way. So visually it looks like two boxes when really is is only one.

And the extra margin problem is still there.

Here is the CSS:

h1 {
margin: 0 0 40px 0;
}
#cont {
margin: 40px;
padding: 5px;
border: 1px solid gray;
}

#nav {
margin: 0px;
padding: 0px;
height: 20px;
float: left;
width: 100%;
background: #ccc;

}

#panel {
background: #CBC5D0;
padding: 10px;
margin: 0;
clear: both;
}

And here is the HTML:

<div id=”cont”>

<h1>IE 5/6 Float Test: This headline has 40px bottom margin</h1>
<div id=”nav”>This div is 20px high and floated left</div>
<div id=”panel”>This div is cleared</div>

</div>

I am only testing this in IE5.5/Win and Moz 1.4/Win. Not sure how IE6 or other browsers handle thisbut IE5.5 sure does some strange things to it.

ic said on November 24, 2003 10:06 PM

Andy,
it has to do something with width:100%.
Try to change it to 99% - and everything is OK.
Also, if you wrap both divs into the third one with
width: 100% everything is fine too.
Weird. Yet another but?

Tim said on November 24, 2003 10:10 PM

Jason,

IE6/Win does the same thing.
Opera 7.2 renders the width ok, but messes up the margins.

There was mention of this in the article Tom Clancy pointed out (see above):

“In fact, both Explorer and Opera 7 already do “auto-enclose” nested floats, in violation of the specs. Opera 6 did not do this, so it seems Opera is following Microsoft’s non-standard lead.”

Ben said on November 24, 2003 11:56 PM

shift the margin from the h1 to the #nav div?
Don’t ask me why …

h1 {
margin: 0;
}

#nav {
margin: 40px 0 0;

Tim said on November 25, 2003 9:47 AM

“In fact, both Explorer and Opera 7 already do “auto-enclose” nested floats, in violation of the specs. Opera 6 did not do this, so it seems Opera is following Microsoft’s non-standard lead.”

Does Opera 7’s rendering depend on which doctype you use?

Tom Clancy said on November 25, 2003 6:23 PM

“Does Opera 7’s rendering depend on which doctype you use?”

Looks like yes: http://www.opera.com/docs/specs/doctype/

There isn’t an exact match for the DOCTYPE Andy is using, but XHTML is supposed to put it into “standards” mode. There’s also this tidbit:

“When XHTML is delivered as text/html and includes the XML declaration, Quirks mode is triggered for IE and Opera 7.0-7.03. From Opera 7.1 on, the XML declaration, PIs, and comments are ignored for determining display mode. When delivered as XML, both Opera and IE use Standards mode.”

Stephen Sequeira said on November 25, 2003 9:59 PM

What role does padding play? Does it behave in the same manner?

P.S. Thank you Andy for the excellent write-up. It was very helpful.

Andy Budd said on November 26, 2003 10:07 AM

Hi folks,

Thanks for all the feedback. I’m glad you found the posting helpful and am grateful so many people had a look at my little issue.

IE’s behaviour in this case is definitely odd and and I basically wanted to try and work out what it was doing, why it was getting things wrong and whether this was a known bug or not.

As to the real case, I basically fixed the problem in IE by removing the float. The other viable option would have been removing the bottom margin on any element that was likely to come before the floated element.

However it has made me aware of how dam inconsistent IE5 and IE6 is when rendering even very simple CSS layouts, and the fact that there are probably loads more undocumented bug’s floating around out there still waiting to be found and documented.

Ah, the joy of CS support.

Mario Dolce said on January 2, 2004 1:04 AM

” Here’s a little problem I’ve been struggling with.

http://www.message.uk.com/test/float-test.html

If anybody can shed some light on this one there’s a pint in it for you, next time you visit Brighton :-)”

Andy are you sure this is the right URI? I would like to take a look at it.

Cheers

Kama Sutra said on January 11, 2004 7:48 PM

gadf

Dotan Cohen said on September 23, 2005 4:38 PM

Does anyone know if this also applies to the margins of the page? I’m trying to make a page with no left/right/top margins, but no amount of testing has left me satisfied. To make matters worse, Opera 8.x gives me different margins than does Firefox 1.0.x. I can’t test on IE because MS does not make a version for linux (Fedora Core 4).
Thanks.

Dotan Cohen

Andy Budd said on September 25, 2005 12:38 PM

Margin collapsing happens on all vertical margins, including those on the body element. However it doesn’t happen on horizontal margins.

Marko Petkovic said on September 27, 2005 3:22 PM

This was just what I was looking for. Helped a lot. Thanks.

Juan Medina said on March 18, 2006 5:10 PM

I’m fairly new to CSS, and just when I thought I’ve grasped enough to make my first client Standards design, BOOM! my margins were all messed up in Firefox. After breaking my head for nearly an hour, I found a name for my problem, margin collapsing, I needed to understand how it worked and fix it, you explained it in a short, but very informative way, problem fixed! Thanks!

Dustin J. Cox said on March 27, 2006 6:35 PM

I found a fix for margin collapsing by reading the W3 specs…

[code]
div {
overflow: auto;
width: 100%;
}
[/code]

that will fix div’s from collapsing - but know that overflow: auto causes problems in ie5 pc - overflow: hidden works in all browsers that I am aware of. I may run across other problems, but I just wanted to share - it was driving me nuts!

Dustin