CSS - Auto-height and margin-collapsing
...or..miraculously shrinking containers!
If you are working with CSS on a regular basis, you might have come across this behaviour. As did I. And if you are like me, you might have found a solution that works around it. But I have to admit that I never really understood what was happening.
So here is the first part of this problem. Consider the following markup:
<p>This is a paragraph within a <code>div</code></p>
We'd have the following stylesheet:
border: 1px solid #EB6B0E;
I am sure that by quickly having a look at the markup and the styles most of you would expect the following (even I did that):
But, surprisingly, in a modern browser it renders like that:
You can have see this in action by checking out Example 1.
What triggers this behaviour? There are two aspects coming into play here. One of them being the behaviour of margin-collapsing. This means that correctly implemented user agents collapse vertically adjacent margins. Using our example from above that is to say that the top border edge of our div is 20px away from a preceeding or its containing element (assuming that any eventual preceeding element hasn't a bottom margin that is larger than 20px, which isn't the case here. Let's also assume and state that our div is the first and only child of
body.) and not 30 pixels. So the smaller of the two margins is
eliminated in favour of the larger. This is illustrated in the following figure:
Fair enough, you'd probably say, but why isn't the paragraph 20 pixels away from the div's top outer border edge like shown above in figure 1? Why does the paragraph's margin "stick out" of the div?
Before answering this question I am going to provide you with a figure that shows you the complete box model for reference (okay, maybe not complete, I focused on the vertical axis, since that is all that matters here):
Now, the answer to our questions from above is derived from the fact how the height for a block-level element is calculated in CSS. If such an element has no height defined, it
will be set to
auto, which is the default height for block-level elements. Thus, in our example the height of the div is per definition the distance from the outer top border edge to the outer bottom border edge of the paragraph. Any vertical margin of the paragraph therefore will stick out of the div. The following two figures try to illustrate this. The first one shows the paragraph's box, while the second one outlines the beforementioned calculation of the div's height.
How would we then avoid the collapsing of the topmargins? I want the paragraph being 20 pixels away from the div's top border edge, how would I do that? The solution to this question lies in the question already. We have to change the height of the div by either adding a border or a padding. Let's add the following to our above styles:
This little addition now completely changes how the height of the div (which is still
auto) is calculated. Now its height will be
the distance from the outer top margin edge to the outer bottom margin edge of the paragraph. The same would have been valid if we had added
a top and bottom border instead of top and bottom padding. See the illustration below or
check out Example 2.