Cooking with Webstandards! Taste the full flavour of the Web


Absolute and Relative ConfuSionS

You might be familiar with the CSS concept of containing blocks. If not, you probably would like to read the following articles discussing this topic in depth:

  1. Explaining CSS positioning in greater detail, Kynn Bartlett
  2. Making the Absolute, Relative, Doug Bowman
  3. Definition of Containing-Block, W3C CSS 2 Recommendation

Per default, if nothing else is defined, the initial containing block is your document's root element (the html element). This means all offset or positioning values will be calculated respecting the boundaries of this element.

Recently, I tried to create a three-column layout using absolute positioning. The general design-frame I tried to accomplish is shown in the following image:

So this comes down to the following markup:

<div id="headmast"></div>
	<div id="content"></div>
	<div id="left"></div>
	<div id="right"></div>

Now, since the header has to remain flexible in height (for example if font-size is increased or the browser window is narrowed) I couldn't come up with an exact top offset for the absolute positioned sidebars (#left and #right). So I had to create a new containing block for the 3 column elements:

<div id="headmast"></div>
<div id="main">
	<div id="content"></div>
	<div id="left"></div>
	<div id="right"></div>
</div>

In order to make this new div-container a containing block we have to apply the following style declaration to it:
position: relative

So, let's come up with some basic styling and positioning for our three columns, resulting in the following CSS:

div#main {
  position: relative;
}
	
div#left {
  position: absolute;
  left: 0;
  top: 0;
  background-color: #E6C187;
  width: 200px;
}

div#right {
  position: absolute;
  right: 0;
  top: 0;
  background-color: #E6C187;
  width: 200px;
}
	
div#content {
  background-color: #D3DBEB;
}

This would result in something you can see on the following picture. Otherwise, you can view it live looking at Example 1.

Since we don't want the content to run "underneath" our left and right column, we have to add some margins to our content div, thus respecting the widths of the absolute positioned columns:

div#content {
  background-color: #D3DBEB;
  margin-left: 220px;
  margin-right: 220px;
}

Adding these lines, we should already have achieved something similar to the first image shown above. Technically and according to the specs, we have. However, this is the point where our all-beloved webbrowser Internet Explorer starts to choke (surprised, anyone?). While the page looks as expected in some other browsers, IE6 comes up with something like this:

You can verify this by firing up Example 2 in Internet Explorer.

I had a really hard time with this broken behaviour and nearly abandoned the whole concept. But then finally I used the debugging rule number one and started to add borders to my div containers. And there you have it! It all came down to one single line:

div#main {
  position: relative;
  border-top: 1px solid #fff;
}

See Example 3.

This method obviously has some drawbacks:

  1. It would render a visible border on any textured backgrounds.
  2. You won't be able to "stick" the three columns directly to the headmast, cause there would always be a one pixel dividing line between it.

In my case both points were to disregard. Fine, I thought, just to discover the next flaw within minutes. Woe!

I tried to add a floated image to the #content div. It didn't show up. I just caught a glimpse of it when reloading the page but it vanished within milliseconds. Believe it or not, the image was rendered beneath the content div and therefore covered by the background color of it.

These are the styles I used for the floated image, nothing uncommon as you see:

*.imgleft {
  float: left;
  margin-right: 10px;
}

See Example 4 using IE6.

After some desperate efforts to make it work, this let me drop the whole thing, coming up with a fixed height headmast and appropriate calculated top offsets for the absolute positioned elements. Not at all satisfying.

However, I kept it in mind and started to play around with it again yesterday. Finding a solution was more an accident than proper thinking. I added one line to the *.imgleft selector:

*.imgleft {
  float: left;
  margin-right: 10px;
  position: relative;
}

I've demonstrated this in Example 5. I am not sure if it is generally a good idea to combine float and position or if it is even correct. It validates, but somehow it feels wrong. Nonetheless, it works in all browsers I tested it (IE6, Mozilla 1.6 and Opera 7.5)

I am quite sure all of these points are widely known, but it was something new for me, so I felt the need of writing it down!

Posted by Minz Meyer at June 04, 2004, 05:58 PM | To Top

Other ingredients

Use a conditional comment for IE to apply the top border to #main. That way only IE gets the border, since only IE needs it!
<!--[if IE]>
<style>
div#main {
border-top: 1px solid #fff;
}
</style>
<![endif]-->

Posted by: Dante Evans at June 12, 2004 07:43 PM | Let Cool (this ingredient)

Yes, another way to make the additional styles available for only IE is to use the
* html selector.

So you can keep your styles inside a linked stylesheets.
Thanks for the pointer anyway, Dante.

Posted by: Minz Meyer at June 14, 2004 05:42 PM | Let Cool (this ingredient)

One of the most valuable lessons I learned regarding CSS-positioning is that you can have an absolutely positioned element inside a relative container - this opens up all sorts of possibilities.

BTW thanks for checking out my redesign yesterday, I think it should all be fixed now.

Posted by: Jim at June 19, 2004 02:44 PM | Let Cool (this ingredient)