TJKDesign: Home Page

ez-css Putting the 'less' in table-less layouts. css-101 logo
Bookmark this article at these sites:

Dodging the Box Model and other Oddities

Box Model, floats, margin collapsing, margin doubling and MSIE5 Win shrink wrapping...

Box Model

Just in case you're wondering what the hack is wrong with the Box Model, let me remind you that IE5 Win does not apply borders and padding the way other browsers do. A "good" browser adds border and padding to the width/height of the box while IE5 Win - a bad browser - does exactly the opposite; it subtracts border and padding values from the box itself. And this is ugly.

For example, Figure A below demonstrates this difference in the rendering of the following CSS rule:

#myBox {
width:100px;
height:100px;
padding:20px;
border:5px solid #000;
background-color:#fff
}

#Figure A

So how bad is that?

Figure B shows that the IE5's box is constructed inside the original size values. This difference in terms of width and height between the 2 boxes is always equal to the sum of any border and padding values declared.

Figure B

In this case:
padding-left [20px] + padding-right [20px] + border-left [5px] + border-right [5px] = 50px

Understanding this logic helps to figure out the discrepancies in terms of width/height values. Those who are using the Tantek Çelik's Box Model Hack for example, are used to doing these calculations all the time. Using such hack, they end up writing at least 3 size declarations per element. IMO this is insane, why use complicated CSS hacks to solve an IE5 problem when we have CC? Read about Conditional Comments...

Anyway, there are other solutions to the MSIE5 Win broken Box Model and they do not imply duplicating CSS size declarations. It is just a matter of building the box without mixing size with border, nor padding.

For example, IE5 Win renders the 2 boxes below as any other browsers do. Displaying a square box of 150px by 150px with a 5px border enclosing a "nested" box that is 100px by 100px.

<div style="border:5px solid #666;padding:20px">
<div style="width:100px;height:100px">
</div></div>

 

<div style="border:5px solid #666">
<div style="width:100px;height:100px;margin:20px">
</div></div>

 

As you can see - in both examples - we did not use border and/or padding with width or height declarations in the same rule. For our first solution, we applied padding to the parent element and for the second solution we've used margin with the nested element.

Be aware that this technique may create extra elements (divitis).

Note: To simplify, I removed the declarations that do not pertain to the demonstration [including background-color].

Floats

Working with "floats" can be confusing. You may experience weird layout "behavior" using them, but you need to know that most of the time, floats behave the way they are supposed to and that the "weirdness" is normal stuff; it is not the result of buggy browser support.

Look at this one! Does it look like a box inside another box?

 

This is the markup:

<div style="border:1px dotted #666;padding:20px;background:#fff">
<div style="float:left;width:50px;background:#999">
</div></div>

If the nested element seems to stick out of the outer box it is because it is in fact out of the box. And that's the beauty of it; this is a powerful feature for the Designer.

Consider this now:

 

This is the markup:

<div style="border:1px solid #666;padding:20px;background:#fff">
<div style="float:left;width:50px;height:50px;background:#999"></div>
</div>
<div style="border:1px solid #ccc;padding:20px;background:#fff">
<div style="float:left;width:50px;height:50px;background:#ccc"></div>
</div>

As you can see, the 2nd outer box goes under the first float to "reach" the preceding element in the flow, while the second "floater" comes next to the previous one. This is standard behavior - that's what floats do.

»» Things you should know re: this markup:
Because the 2 outer boxes are "empty", IE5 Win positions both of them with the same x-y coordinates and with the 2 floaters next to each other. For the same reason, IE5 Mac does not stretch the outer boxes across the frame (the window). Also, IE5 Mac renders the 2 floaters as it is supposed to but the second outer box is misplaced. Its x-coordinate is equal to the x-coordinate of the nested floater minus the padding declared.
IE Win shrink wraps the box to fully contain the floater if we apply any width or height declaration to the outer box.

The CSS property that let's you "put an end" to the float "behavior", is "clear". Using clear, on any element, will make this element position itself right below the floater it is supposed to clear (depending on the given attribute's value - left, right, etc.).

The most common ways to clear a floater using structural hacks are as follow:

<p style="clear:left;height:0;line-height:0">&nbsp;</p>
<div style="clear:left;height:0;line-height:0">&nbsp;</div>

To minimize impact layout across browsers, I prefer to use this one:

<div style="clear:left;height:0;line-height:0"><img src="clear.gif" alt="" /></div>

Below, to clear the float and demonstrate some browser inconsistencies, I'm using the first one of these hacks replacing the line-height and height declarations with a margin and background-color declarations.

 

This is the markup:

<div style="border:1px solid #666;padding:20px;background:#fff">
<div style="float:left;width:50px;height:50px;background:#999"></div>
<p style="clear:left;margin:20px;background:red">&nbsp;</p>
</div>

»» Things you should know re: this markup:
IE Win and Opera apply the top-margin to the element but in other browsers, the element that clears the floater touches it; the top-margin being ignored (this is standard behavior).

One more thing about floats: a float nested in another float will always be fully contained by its parent float. Like this:

<div style="float:left;border:1px solid #666;padding:20px;background:#fff">
<div style="float:left;width:50px;height:50px;background:#999"></div>
</div>

 

»» Things you should know re: this markup:
NN6 ignores the specification, Opera 7 contains the nested box vertically, but not horizontally (which, I guess, is OK).

Margins collapsing

I just showed you different ways to clear floats, and maybe you wondered why these techniques didn't rely on empty elements; why are we using a "&nbsp;" or a 1*1 pixel image in there?

Following is the preceding example, where we used the structural hack to clear the box:

<div style="border:1px solid #666;padding:20px;background:#fff">
<div style="float:left;width:50px;height:50px;background:#999"></div><p style="clear:left;margin:20px;background:red">&nbsp;</p>
</div>

 

Below is what happens when we remove the spacer ("&nbsp;").

 

»» Things you should know re: this markup:
Opera 6 does not clear the inner box.

This is an example (not a perfect one) of "collapsible margins". Because the element is empty, its vertical margins meet and then collapse. Note that horizontal margins never collapse.

As I said, this is not a perfect example because there is a cleared element involved in this markup, and it makes the behavior of margins collapsing even more confusing. Why? Because the W3 says "The resulting margin width is the maximum of the adjoining margin widths"; and as you can see here, there is no margin width at all below the grey box (the white space corresponds to the 20px padding).

So we'll go with plain vanilla boxes like the ones below, using the following markup:

<div style="background:#fff;margin:20px"><div style="background:#ccc;margin:20px">&nbsp;</div></div>
 

This should show no vertical margin to separate the nested box from the outer box. This is normal behavior.

In fact, margins collapse for 2 reasons:

  • The element is not floated nor positioned (according to the W3C, "absolute" or "relative" doesn't matter).
  • There is nothing between the margins of the elements.

To correct the problem without positioning the elements, there are two easy fixes.

1. We apply a border to the outer box:

<div style="border:1px solid #000;background:#fff;margin:20px"><div style="background:#ccc;margin:20px">&nbsp;</div></div>
 

2. We apply some padding to the outer box:

<div style="padding:1px;background:#fff;margin:20px"><div style="background:#ccc;margin:20px">&nbsp;</div></div>
 

To understand why we applied both fixes to the outer box and not the inner one, we need to picture the Box Model Hierarchy. Borders and padding areas are inside the margins of a box, not outside of it, therefore applying borders or padding values to the nested box would not prevent the margins of said box to come in "contact" with the margin of its parent element.

IE5 Win shrink wrapping

If you're browsing this page with IE5 Win, you've noticed that our two "tricks" didn't fix the collapsible margins issue. This is because IE5 Win is "padding-challenged".

In fact, this browser shrink wraps almost everything. This is a well known issue and most of the Designers use a "height:1px" CSS declaration as a fix. I'm using a "shorter" version, myself, as in the rule below:

<div style="height:0;border:1px solid #000;background:#fff;margin:20px"><div style="background:#ccc;margin:20px">&nbsp;</div></div>
 

Obviously, it is necessary to use an IE Conditional Comment or a CSS hack to hide this height declaration from non-IE Win browsers.

Margin Doubling

This is an IE Win "special". This browser may double the horizontal margin in a float (depending on margin side and float direction). According to the markup below, the small gray box in the following example should have the same width as the white margin on both sides of it. But this is not true in IE Win; in this browser, the margins are twice the size of the small gray box.

<div style="border:1px dotted #666">
<div style="float:left;width:50px;height:50px;margin:50px">
</div></div>

 

To correct this strange behavior, one needs to add "display:inline" to the CSS rule. If you are viewing this page with IE Win, you should see that the box below "got its acts together".

<div style="border:1px dotted #666">
<div style="display:inline;float:left;width:50px;height:50px;margin:50px">
</div></div>

 

»» Things you should know re: this markup:
Opera 7 is totally unable to nest the floater.
NN6 does not display the boxes one inside another. The outer box collapses and the gray box sits 50px below. To prevent such behavior, it is safe to add a structural hack to clear the floater from inside the outer box, like this:

<div style="border:1px dotted #666">
<div style="float:left;width:50px;height:50px;margin:50px">
</div><div style="clear:left;height:0;line-height:0"><img src="/img/clear.gif" alt="" /></div>
</div>

 

»» Things you should know re: this markup:
In Opera 6 the right margin collapses against the inner box. In Opera 7 it is the whole outer box that collapses totally.

Resources

This is a nice diagram to help you picture the Box Model Hierarchy.
This is a very complete article on Margin Collapsing

From the horse's mouth:
Box Model
Collapsing Margins

Tantek Çelik's Box Model Hack vs. IE CC...

This link shows you how to install multiple versions of IE on the same PC. Be aware that these versions identify themselves as the most recent version installed. So you'll have to take this in consideration when testing documents that include IE CC.
»» On the CSS-D list, Manfred Staudinger suggested renaming the registery key (IE) in \Software\Microsoft\Internet Explorer\Version Vector\ to differentiate Version Vectors 5 and 6. This solution seems to work fine, but be aware that tweaking the Window's Registry may be hazardous. If you decide to go this route, you're on your own...

Screen Ruler is a nice little program, visit Micro Fox Software.