CSS Tutorial
CSS Layout: Part One
Float and Clear Properties
So far you have only encountered normal content flow. There are two types of non-normal flow: floating and positioning. First, we will consider floating. When you float an element it becomes a block box. A floated block box is laid out according to the normal flow, then taken out of the flow and shifted to the left or right as far as possible. The float options are "left", "right", "none", and "inherit".
The W3C says,
A floated box must have an explicit width (assigned via the 'width' property, or its intrinsic width in the case of replaced elements). Any floated box becomes a block box that is shifted to the left or right until its outer edge touches the containing block edge or the outer edge of another float. The top of the floated box is aligned with the top of the current line box (or bottom of the preceding block box if no line box exists). If there isn't enough horizontal room on the current line for the float, it is shifted downward, line by line, until a line has room for it.
The width of any floated element (box) must be declared unless it is intrinsic as in the case of an image or Flash object, otherwise called replaced elements. Height is calculated automatically to allow text wrap, though you may specify it. But if the image exceeds the width or height of the content area, it will distort the box unless you hide the overflow (see below on how to do that).
The Float Property
The float property determines which side of the item will allow content to flow around it. If the image is floated to the right side of its container, text will flow to the left of the image. If the height of the text exceeds that of the image, the text will flow under the image. Conversely, if you float an item to the left, you flow content around its right side. The following code will float an image to the right within a paragraph. The text will flow around the left side of the image.
CSS:
p img {float: right; margin: 10px;}
HTML:
<p>Nullam lacus nunc... <img src="thisImage.gif" alt="" /></p>
If, on the other hand, you float both the text and the image separately, they each flow in discreet boxes. This is the type of floating that you are far more likely to use in creating layouts without tables. Let's suppose you have the following CSS and HTML.
CSS:
#menu {float: left; width: 200px;}
#main {float: left; width: 600px;}
HTML:
<div id="menu">Text here...</div>
<div id="main">Text here...</div>
Floated elements will sit side by side until they run out of room and go to the next line. That is, they will keep floating until the float is shut off, or cleared. Let's suppose you want to float 3 product images side by side on a products page. You need to get the items to sit in a row, and then create a line break to start a new row. There are at least two ways of doing this. This introduces the clear property.
The Clear Property
First, you can wrap each item with a DIV element and call a class that uses floating. Then after each row you could use a <br /> that uses the clear property to break the float.
CSS:
.product {float: left; width: 60px; margin: 0 15px 15px 0; padding: 5px;}
.clearboth {clear: both;}
HTML:
<div class="product">product 1</div>
<div class="product">product 2</div>
<div class="product">product 3</div>
<br class="clearboth" />
The second method is to wrap your row in another DIV element that contains it, and clear that container DIV element.
CSS:
.productWrapper {width: 500px; clear: both; margin: 10px 0;}
.product {float: left; width: 60px; margin: 0 15px 15px 0; padding: 5px;}
HTML:
<div class="productWrapper">
<div class="product">product 1</div>
<div class="product">product 2</div>
<div class="product">product 3</div>
</div>
There is another method. If you wish, for example, to float a left nav and a main panel you could float only the former DIV and set the left margin on the main DIV so that it will not tuck under the former.
The clear property prevents wraparound, effectively creating a break. Without the clear property floats would float indefinitely. In the examples above, we want a complete line break, so we set clear to "both", though "left" will often suffice. Incidentally, this second method is superior in one sense, in that it is more semantically correct because a <br /> tag is often a formatting gimmick, and we want to separate formatting from markup.
An element can float and clear at the same time. Let's suppose you are floating some DIV elements to the left. If you also clear any of these to the left, the cleared float will start on a new line and the following boxes will continue the float. Conversely, right-floated elements can be cleared with "clear: right". Clearing to the right has no effect on left-floated elements, just as clearing to the left has no effect on right-floated elements. So use "clear: left" with "float: left" and "clear: right" with "float: right". However "clear: both" breaks all floats. Note that IE7 has trouble with this technique. If, for example, you float labels and form elements side-by-side, and clear the labels to the left, the form elements will not behave as if the labels have cleared, but will keep floating on one line until they run out of room. You will need to coerce IE, perhaps setting a width on the container so that they do not have room to float. I tend to use an ordered list (OL) for this purpose, wrapping a label and form element within a single LI, and set the LI to "clear: both".
There are difficulties in getting a container to appear around floated elements. If you have, say, two elements side by side, each floated to the left, and also a DIV element that wraps the two with a 1 pixel border, you will find that the border does not actually wrap around the floated boxes. The wrapper border will need to be coerced. This is not a bug, but is default behavior because if you, for example, have a large image floated within a short paragraph, you very likely want the following paragraph to start right under the former rather than leaving a blank space until the image is cleared.
The first method is to float the containing element. The second method is to use "overflow: auto" on the container element. This can sometimes cause problems in that under certain conditions scrollbars will appear in the wrapper DIV. Another method is to use "overflow: hidden", but again, undesirable results may happen.
Yet another method uses the "after" pseudo-element. The second declaration in the example below is a hack, if you will, for compliant browsers, which involves the use of some CSS 2.1, which we will cover later. (The third declaration should not cause any confusion except for the display property. This is to correct the IE Doubled Float-Margin bug, which we will discuss in brief below.) However, you will still need extra coercion for IE8, such as "overflow: auto" or overflow: hidden". IE7 does not need this coercion but only needs hasLayout triggered, such as putting "zoom: 1" or "height: 1%" in the wrapping DIV. If you use a meta tag to force IE8 to render like IE7, then the you could dispense with the overflow property should you find its use unacceptable.
CSS:
div.wrap {margin: 10px; width: 204px; border: 1px solid red; clear: both;}
div.wrap::after {content: "."; visibility: hidden; height: 0; display: block; clear: both;}
div.box {display: inline; float: left; width: 60px; border: 1px solid #333; margin: 3px;}
HTML:
<div class="wrap">
<div class="box">box</div>
<div class="box">box</div>
</div>
My preference is to use "overflow: hidden" in the containing DIV whether or not I am forcing IE8 to render as IE7. It saves a line of CSS by dispensing with the "after" pseudo-element declaration, and it does not require a hasLayout trigger.
The Box Model
Understanding the box model is critical to developing web pages that don't rely on tables for layout. In the early days of writing HTML, before the advent of CSS, using tables was the only way to have discreet content in separate boxes on a page. But tables were originally conceived to display tabular information. With the advent of CSS floating and positioning, there is no longer a need to use tables for layout, though many years later many, if not most, sites are still using tables in this manner.
The box model, as defined by the W3C "describes the rectangular boxes that are generated for elements in the document tree and laid out according to the visual formatting model". Don't be confused by the term "boxes". They need not appear as square boxes on the page. The term simply refers to discreet containers for content. In fact, every element in a document is considered to be a rectangular box.
Padding, Borders, Margins
Padding immediately surrounds the content, between content and borders. A margin is the space outside of the borders. If there are no borders both paddng and margin behave in roughly the same way, except that you can have negative margins, while you cannot have negative padding. Also padding does not collapse like margins. See below for the section on collapsing margins.
The picture on the right illustrates padding, borders, and margins. The content area does not really have a border. The line around the content merely indicates the limits of the actual content.
Traditional vs. W3C Box Models
So how do you declare these properties in your CSS, and how do you set the width of a box? That depends on the box model. There are actually two box models. The traditional box model is supported by IE5.5 and previous versions of IE, and any version of IE in quirks mode. It states that the width of a box is the combined width of the content, its padding and its borders. Imagine a literal box that you can hold. The carboard exterior is the border. We don't care about the content inside of the box. It may fill up the box entirely or have space around it. If it has space around it, that is its padding, which sits between the content and the exterior (border) of the box. But according to this model, it does not matter what the actual content width is. The width of the box is what matters. Using the traditional model let's consider the following declaration.
.box {width: 200px; border: 1px solid black; padding: 10px;}
In the traditional model the width of the box is 200 pixels. We don't care how big the border (exterior) is, what the padding is, or even the width of the contents. This model is arguably very logical, but is not the model supported by compliant browsers. Peter-Paul Koch makes an excellent argument that this is what the box model should be. As it is, the traditional box model is deemed to be buggy and calculations must be adjusted in compensation. Moreover, at the time of this writing most developers are not supporting IE5, so a presentation of the traditional model might seem to be only a point of curiosity. However, I think it worthwhile to understand the two models. When we get to CSS3 we will meet the box-model property which will let us choose models. So ultimately this issue may be revisited.
The W3C model, on the other hand, which is supported by all compliant browsers, defines the width of the box as the width of its content plus left and right padding, and left and right borders. So with the declaration above the box will occupy 222 pixels of horizontal space, or from left to right the calculation will be left border (1px), left padding (10px), content width, right padding (10px) and right border (1px).
Now what about margins? Aren't they part of the box? Technically no. They sit outside of the box, but it's okay to act as if they are part of the box because margins have to be accounted for. So you will often see margins treated as part of the box model. The border, if declared, is the actual exterior of the box. If there is no padding or border, the content is the box, or to put it another way, the content and the box coincide. Another way to look at this is that in the traditional (IE5.5) model, the declared width is the width of the box, that is, the end of the calculation before margins are added. By contrast, in the W3C model the declared width is the beginning of the calculation before padding and borders are added, as well as margins.
To return to our former example above, if we have 10px of margin on either side of the box, the box would still be 222 pixels wide, but we would need to allow for 242 pixels so that the box does not break out of its container. This is why many just say that the box is 242 pixels wide.
A practical and common example should clarify this principle. Let's say that you have a page that is 760px wide. You want to assign a width of 200px to the left menu and 560px to the content pane. That will work unless you have a border, say, on the right side of the left menu, or unless you have padding in the DIV elements, which you will probably want to have to let your content breathe. If you assign a 1px border to the left menu, and also assign 6px for left and right padding in each pane, you will need to reduce the declared width of the DIV elements to allow for the extra space in the W3C model.
CSS:
#wrap {width: 760px;}
#menu {float: left; width 187px; padding: 6px; border-right: 1px solid #999;}
#main {float: left; width 548px; padding: 6px;}
HTML:
<div id="wrap">
<div id="menu"></div>
<div id="main"></div>
</div>
The math from left to right would be:
menu left padding + menu content + menu right padding + menu border + main left padding + main content + main right padding (or in pixels) 6 + 187 + 6 + 1 + 6 + 548 + 6 = 760
But since IE5 has the traditional (non-W3C) box model, that browser does not calculate width using the content area as a starting point, but rather using the content area plus padding and borders, that is, the visible box. Nevertheless, even for IE6 you may have to accommodate another issue. For instance, consider the following CSS. Don't worry if you do not understand all the code.
CSS:
#container {width: 606px;}
.box {width: 200px; float: left; margin: 1px; padding: 0; border: 0 none;}
HTML:
<div id="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
This will not break in Firefox, but will break in IE6. In the container you are allowing 606 pixels. Each of the boxes is 202 pixels wide when you add the width of the content plus the left and right margins. Yet the last box will wrap to the following line in IE6 unless you set the container width to 607 pixels. Why is one extra pixel needed in the container? It is due to the IE Doubled Float-Margin bug which is discussed at Position Is Everything. Fortunately, the fix does not entail adding pixels to the container, but making the floated elements display inline (see below).
Margin Collapse
Vertical margins collapse when they meet. Though it may seem like a strange thing, if you have assigned top and bottom margins to the P element of, say, 10px each, you will not have 20px of margin between paragraphs, but rather 10px. This is considered to be desirable and expected behavior, and not a bug. Now consider the following declaration.
p {margin: 10px 0 16px;}
In this case the space between paragraphs would be 16px, that is, the greater of the two values. Margin collapse does not occur when either box is floated, when one element uses the overflow property set to any value other than "visible", with absolutely positioned elements, with elements whose display property is set to "inline-block", or if the child element is cleared.
You can override margin collapse also by adding a border, of the same color as the background if you want it unnoticed, or by using padding instead of margins. Eric Meyer has a nice description of collapsing margins. In sum, margin collapse is meant to prevent certain design problems, and yet is not difficult to override.