CSS버그, Auto Height & Margin-collapsing

Some of you may be aware of the fact that I am co-teaching a CSS-Workshop over at IWA/HWG. This week two students ran into exactly one and the same problem so that I had to find an explanation. So this entry is basically meant for them.


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:



We’d have the following stylesheet:



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.




Another example involving a floated element!


The following is a simplified reconstruction of what happened to some of my students in the workshop. The given markup would be something like this:



Furthermore, we apply the following styles:



Let me show you the rendering results of three different browsers first:


Mozilla rendering
Mozilla 1.7


Opera rendering
Opera 7.5


Internet Explorer rendering
Internet Explorer 6


You can check your own browser by looking at Example 3.


Well, that’s pretty neat, isn’t it? In fact, that is why we like CSS so much 🙂


But given our markup and our style declarations, Mozilla does it correct. Under certain circumstances (I’ll explain that a bit later) Opera is correct either. Internet Explorer (surprise, surprise) is wrong here.


At first glance I thought to myself that Mozilla must be wrong. It seemed to clearly violate the float rule number 8, which states:


A floating box must be placed as high as possible.


So, Opera must be right, and even IE is coming close. This second thought alone should strike all alarm bells! Let us see what else is available. Take a look at float rule number 4:



A floating box’s outer top may not be higher than the top of its containing block.


So it all comes down to one thing again. The containing block in our case would be body. Where is the top of body and what is the height of body? Take a look at our body styles. There is not much in there, just a font declaration. So the initial values for margin, padding and border will be assumed. Those are 0, 0 and none which means no margin, no padding, no border.


If we now consider what we have learned in our first example, the height of body will be the distance from the outer top border edge to the outer bottom border edge of its normal-flow block-level child (= the content div), while the margins of that child will again “stick out” of it.



But how can Opera render it completely different and nonetheless be correct? In this special case, you have to be aware of the fact that most browsers have their own user agent stylesheets implemented. These stylesheets rule the rendering of hyperlinks for example, or the font family and size that is displayed on completely unstyled pages. Additionally, they set an explicit value for margin and padding for body.


Since we have declared neither in our styles, our initial values will be outruled by the user agent’s styles. Now the main point here is, that Opera has different styles than Mozilla and Internet Explorer. The Opera styles declare:



On the other hand, Mozilla and Internet Explorer assign the following styles:



This is only a slight difference, but it has a lot of impact on our example design. Let us copy Opera’s styles and add the following to our working example:



Since there is now a padding, the height of body will change. It will be now the distance from the outer top margin edge to the outer bottom margin edge of its normal-flow block-level child.



This last example is also available. If you wish, please have a look at Example 4.


If the above illustrations are too small for your eyes, I have created a page containing all of them at a higher resolution.


이 글은 스프링노트에서 작성되었습니다.