Explaining CSS Positioning in Greater Detail

Originally written by Kynn Bartlett, edited by Minz Meyer

The "containing block" is the "field" (like a cow field) in which the absolutely positioned blocks will be placed.

Initially, the containing block equals the whole document, that is, the "viewscreen" which you see on your page and can access with the scrollbars.

The upper left hand corner of the containing block is the "zero, zero coordinate" for the top and left properties. The lower right hand corner is the "zero, zero coordinate" for the bottom and right properties.

To continue the cow pasture analogy, you initially have a fence that includes all your property. Then you tell the cow, "go stand 10m from the left side of the pasture, and 5m from the top side." If you have a really smart cow, he trots over to the upper left corner of the field. (In CSS layout, the next thing you'll want to do is tell the cow "okay now become 12 meters wide." Obviously this isn't a perfect analogy!)

This is useful if you have a very simple layout. But if you want a more complex layout, you need to change the containing block to be a subset of the original field. This is like building an inner fence within your cow pasture.

You do this by creating an inner <div> and setting it position: relative;. You could also set position: absolute; or even position: fixed; because whenever you reset the positioning from the default, it resets the containing block for everything within that element.

The containing block does not reset automatically, though, just from having a <div> or any other block element; containing block doesn't mean the same thing as "parent element" or anything like that. The only way to designate something as a containing block -- as a "hard fence" in our cow pasture -- is to change the value of the position property.

Okay, so let's say I have the following:

  <body>
    <div id="banner">
      <h1>Welcome to the Cow Pasture</h1>
    </div>
    <div id="columns">
      <div id="main">
        This is my main content.  blah blah blah.
      </div>
      <div id="nav">
        This is my navigation bar.
      </div>
      <div id="side">
        And this is my sidebar.
      </div>
    </div>
    <div id="footer">
      Copyright by the cows
    </div>
  </body>

This snippet of code defines sections of HTML -- which we'll position -- using the <div> tag and CSS. For this particular design, we're trying for this layout:

Layout with banner on the top, three columns (nav, main, side), and footer across the bottom.

Our <div>s are #banner, #columns, #main, #nav, #side, and #footer. You'll notice that I added in an extra one, #columns, that doesn't really appear in our layout diagram. There's a reason for that -- that will be our inner fence, our contaning block. You'll see why it's necessary in a sec.

Let's pretend that #columns doesn't exist, and instead start writing our CSS. We'd do something like this:

  #banner { height: 3em; }
  #main { margin-left: 12em; margin-right: 12em; }
  #nav { position: absolute; top: 4em; left: 1em; width: 10em; }
  #side { position: absolute; top: 4em; right: 1em; width: 10em; }
  #footer { }

Taking each of these in order, the first rule reserves some space for the banner, 3 ems. (An em is a unit of measure equal to the font size of that element; in this case, it will be the size of the user's default font size. Ems are good units to use for layout.)

The second rule puts out big margins on the #main content. This is our "fluid" column; the one on the left will be 12 ems (or a little less, see below), and the one on the right will also be 12 ems, but the middle column, where our main content goes, will be all the remaining space that's left over. The height of the content <div> will be determined normally; in other words, long enough to contain all of the things within it, just like any other block element.

Now we position our #nav and #side bars using absolute positioning. One "cow" gets sent to the upper left, and is told to stand 4 ems in (think of an em as, oh, the length of a stride, in this analogy; short people have smaller strides and tall people have longer strides) from the top, and 1 from the left. Oh, and mark out a space 10 ems wide, meaning into the center of the field (since we've already told him one fixed corner). That's where that cow lives. The other cow goes to the upper right, 4 ems from the top and 1 em from the right, and also 10 ems wide.

Because the #nav and#side elements use absolute positioning, that means that for purposes of where the next thing goes, they effectively don't exist. So the next <div>, the #footer, follows right after the #main. In this case, it's not styled in any particular way, so like any good <div>, it will spread out the full length of its containing block (which is the whole field) and be as tall as is necessary to contain the content.

Now, this is all well and good; and in fact, it pretty much works.

Except for one thing.

What happens if the <#banner> element has content which is taller than 3 ems?

Well, you get overlap. The absolutely positioned #nav and #side bars will be placed on top of the #banner, and your layout will look bad.

That is why we need an additional #columns <div> -- because we're going to create an inner fence where the #nav, #main, and #side columns will be placed. Here's the CSS:

  #banner { }
  #columns { position: relative; }
  #main { margin-left: 12em; margin-right: 12em; }
  #nav { position: absolute; top: 1em; left: 1em; width: 10em; }
  #side { position: absolute; top: 1em; right: 1em; width: 10em; }
  #footer { }

There are only three differences between these style rules and previous set. First, the #banner no longer has a height set. Instead, we'll let it be as big or small as it needs to be, based on what's contained within it, just like any other <div>.

Secondly, we've added a rule for the #columns that only says position: relative;. This means that the #columns div will be laid out normally -- after the end of the #banner -- and then shifted some amount. However, since we don't tell it how much to shift the #columns (which we would do using top, bottom, left or right if we wanted to move it), the #columns stay right where they should be.

So what does this do? Simple: It designates #columns as a new containing block for everything that's within that particular <div>. It designates it as a fence.

How wide is our fence? Well, like any ol' <div>, it's as wide as its containing block (the screen), and as tall as it needs to be hold the stuff inside it.

The things inside it are the #main, #nav and #side sections of our page. The rule for #main is unchanged; it establishes the margins, reserving space for our #nav and #side bars.

The third change, however, is that the top values for #nav and #side are no longer 4 em. That was the position for when we assumed #banner was 3 em tall (and these bars would start 1 em away). Now, we can place them relative to the new containing block -- #columns -- and they are 1 em from the top.

You can see both of these here:

There is one problem with this layout, though.

The height of #columns will be determined by the height of #main -- not by #nav or #side, because they're absolutely positioned, and thus taken out of the normal layout scheme for determining where the next block (#footer) should be placed. This means that if #nav, for example, is longer than #main, it may very well extend beyond the place where #columns ends and #footer begins.

In such a case, #footer would be partially obscured by #nav. The same can happen on the other side with #side. For this reason, I prefer to use this style of layout:

Layout same as above, but footer is within the nav and side columns, rather than below them.

There are three ways to get this effect. The first is to simply give #footer the same left and right margins as #main. (If you do this, make sure that you don't change the font size for #footer; if you want to make it small, make an inner <div> and set that to font-size: smaller;.)

Secondly, you could place #footer within the #columns div, but this doesn't really get you anything, because you'll still have to set the margins as in the previous paragraph. And finally, you could move #footer inside #main itself. You wouldn't have to set the margins then (it would just use the margins of #main.)

The potential problem with the latter approach is that while it may look similar to our layout diagram conceptually, it also moves #footer up before the navigation links and sidebars, in the actual source code of the HTML. This is the order in which they will be read to a screen reader or in a browser such as Lynx which doesn't recognize CSS. In some cases this will be the order you want, and in others it won't be. Keep in mind that most users know what a footer looks like (or sounds like) and may assume that once they hit the copyright notice, they're at the bottom of the page.