Search code examples
csshtmlpositioning

Why does a nested HTML element make my CSS jump?


Here's a puzzle. Basic page, one element:

http://jsfiddle.net/PZj6t/

HTML:

<div id="container"></div>​

CSS:

body, html {
    height: 100%;
    margin: 0;
    padding: 0;
    background-color: black;
}
#container {
    position: relative;
    margin: 0 auto;
    width: 400px;
    height: 100%;
    background-color: #666;
}
​

That one looks how I want, with the #container neatly flush to the top. But when I add a nested element:

http://jsfiddle.net/PZj6t/1/

HTML:

<div id="container">
    <nav id="topnav"></nav>
</div>​

CSS (new):

#topnav {
    width: 100%;
    height: 40px;
    margin: 30px 0;
    background-color: red;
}
​

The container jumps down. It seems that the margin-top from #topnav is somehow being passed to the container, and now the page has a scrollbar I don't want. (I'm testing in Chrome.) How do I prevent this?

(As a further mystery, if I add border: 1px solid white; to the #container's CSS, the jump disappears. Which would be fine, except that also adds two pixels worth of undesirable scroll to the page.)


Solution

  • This is due to a feature of CSS called margin collapsing. If there is no padding or border on a parent element, the parent and its child's margins "collapse" to the greater value of the two and is essentially applied to the parent.

    For your situation, I would suggest simply adding an additional inner wrap within the container, and throwing some padding on it to simulate the margin effect you're looking for: http://jsfiddle.net/PZj6t/3/

    Anything within the #inner div or below should behave as you expect, as margins only collapse when they are at the edge of their parent (and no padding or borders are present).