Adding CSS border changes positioning in HTML5 webpage

I'm having a problem with the page elements moving when I add a border in a HTML 5 document.

I expected the containing header element (grey) to appear at the top of the screen, but it seems to take the margin from the inner div (red). However if I add a border to the header it appears where I expect and the red inner div only moves slightly!

(1st image: without border; 2nd image: with border)

No borderwith border

I have tried setting relative or absolute positioning, using a div instead of the header element, setting margins & padding to 0, using a HTML4 doctype etc. The HTML validates. This is the HTML stripped of everything and still doesn't work. Its happening in latest Chrome & FF.

HELP!! What have I missed?? Or any workarounds (other than keeping the border)?

HTML:

<!DOCTYPE HTML>
<html>
<head>
<title>Test</title>
</head>
<body>
    <header><div id="mydiv"></div></header>
    <div id="content"><p>hello</p></div>
</body>
</html>

CSS:

header {background-color:#CCCCCC; width:960px; height:430px;}
#mydiv {width:960px; height:320px; margin:80px 0px 0px 0px; background-color:#CC0000; }

Solution 1:

The issue comes from something called "margin collapsing". It's simple: 2 adjoining margins collapse to the highest of the two (I say two, but it could be more).

In your case, '#mydivs' margin-top - 80px - is touching the 'header's margin-top - 0px. They're adjoining - there's no element between them, nor padding, nor border.

The margins collapse, therefore, to the highest of the two (80px), and it is then applied on the highest of the elements in the parent-child hierarchy - that's the header in this case.

One solution to this problem is to put something between the margins; either of some padding, or a border on the header works fine.

header {
  border-top: 0.1em solid rgba(0,0,0,0);
}

A second solution (my preferred one), is to make the parent element create a new block formatting context. That way, its margins simply won't collapse with that of its child.

How do you create a block formatting context? There are four possible ways.

  1. by floating it.
  2. "position absoluting it".
  3. adding one of these displays: “table-cell”, “table-caption”, or “inline-block".
  4. adding an overflow other than visible.

To prevent the margins from collapsing you could do any of these 4. I usually go for number 4) - set overflow to auto, as it's only side affect... well it's improbably likely to become a problem.

header {
  overflow: auto;
}

That's basically it for parent-child margin collapsing. There's also margin collapsing between siblings and the rule is pretty much the same: 2 adjoining margins collapse to the highest of the two. It's the solutions that are not.

Here's a great explanation of margin-collapsing - http://www.howtocreate.co.uk/tutorials/css/margincollapsing

Solution 2:

This is known as collapsing margins. They can be overcome by adding padding to the parent element (in this case, the <header>).

Code (with padding, without padding):

Notice the padding:0.001em;. This makes the margins no longer collapse, but doesn't add any space to the <header>.

header {padding:0.001em; background-color:#CCCCCC; width:960px; height:430px;}
#mydiv {width:960px; height:320px; margin:80px 0px 0px 0px; background-color:#CC0000; }

Solution 3:

The link to collapsing margins in bfrohs answer helped me find a solution that will work for me, and it may help anyone else who gets this problem.

Absolutely positioned divs don't collaspe so making the header element relative and the inner div absolute gives the correct positioning without adding space with padding or margins.

Working example: http://jsfiddle.net/8QPGJ/

(Without the positioning: http://jsfiddle.net/8QPGJ/1/)