CSS: clean solution to the margin collapse issue when floating an element

jameshfisher picture jameshfisher · Jun 1, 2011 · Viewed 18.6k times · Source

Example HTML+CSS:

<html>
  <body style="padding: 0; margin: 0;">
    <div style="float: right;">first</div>
    <div style="margin-top: 2em;">second</div>
  </body>
</html>

Desired behavior: the first div floats to the top-right of the window. Actual behavior: it floats 2em below the desired position. Reason: margin collapsing.

Despite identifying the problem, the solutions I can come up with feel like hacks:

  • change body style to margin: -1px 0 0 0; border-top: 1px solid;.
  • insert <div style="height: 1px; margin-bottom: -1px;"></div> before first
  • insert the above <div> between the first and second

Is there a clean, idiomatic way of avoiding this issue?

Answer

kapa picture kapa · Jun 1, 2011

Adding overflow: hidden; to the body should solve your problem. It defines a new block formatting context as described in this article: The magic of overflow: hidden.

jsFiddle Demo (the body tag is automatically added by jsFiddle, that's why I haven't included it in the HTML markup)

UPDATE (thx to @clairesuzy): This solution does not work if body has padding: 0. Until I can find a better way, I can only suggest to add a wrapper around the two divs (at least I deserve now @Marcel's downwote :)), which I still think is cleaner than the solutions posted by the OP. I normally add a wrapper around floated stuff anyways (makes it easier to handle older browsers most of the time), most of the time it does not need to be added deliberately, because it is logically and semantically required.

So for now, I can come up with this:

<body style="padding: 0; margin: 0;">
   <div id="container" style="overflow: hidden;">
       <div style="float: right;">first</div>
       <div style="margin-top: 2em;">second</div>
   </div>
</body>

jsFiddle Demo

UPDATE 2: After thinking it through, and reading comments, I really think that overflow: hidden (or anything other than overflow: visible) on the container is the right solution. The only exception where it did not work for me is setting it on the body element, which is a very rare situation anyways. In these rare situations, you can try using position: absolute; top: 0; right: 0; instead of floating.

Another possible workaround: I have also found that setting display: inline-block; width: 100%; on body works indeed.

jsFiddle Demo