I had to redo this again to support a site with relative links in it. I have a layout that handles the equal height columns and also relative links (and drop shadows to boot).


I'm using a method for sizing the height of elements that I've not seen previously and I think perhaps I can submit it to A List Apart

I'm going to retract the submission because I was unaware that a xml declaration turned on IE's quirks mode. This layout only works in quirks mode and not even in that in IE7. A better, though hackish solution, is absurdly tall divs.

I've redone my sample using the overflow method.


Sizing Elements to the Container's Height

Three column layouts are all the rage nowadays. It's a great way to keep your navigation bits accessible and keep the content from getting really wide and looking all cluttered. Most of time attention is paid only to the widths of the columns, but heights can be important as well. Say, for instance, that you want a navigation bar on the side to go all the way to the bottom of the page. There are seveal methods that are currently being employed to achieve this effect:

What I really want is to be able to say is: "This element should be the same height as the containing element." I was searching the internet for a method to do this and ran across this statement at quirksmode:

"[I]f you want to make an element as high as the entire page (whatever this height may be) you're out of luck. Although it might seem simple the specs (and the browsers' unthinking conformance) make this completely impossible.

"The spec says: 'If the height of the containing block is not specified explicitly (i.e., it depends on content height), the value is interpreted like auto.'"

That's pretty daunting. I like quirksmode and there's lots of good information over there. In fact, when I read that, I gave up and started looking for workarounds. Fortunately however, I have a really bad memory. I ran across the problem a little while later and, before I remembered it is impossible, solved the problem.

As is often the case, the solutions are completely different for IE and CSS compliant browsers. First, to deal with IE:

Sizing to the Container's Height in IE

The solution in IE arises from what happens when an element overflows its boundaries. According to the specification it should simply extend past the bottom unless the overflow property specifies otherwise. In IE the containing block is reflowed to be large enough to contain the content.

If you had the following HTML:

<div class="outer"><div class="inner"></div></div>

With the following style applied:

.inner, .outer
  { width: 20px; height: 20px; border: 2px solid; }
.outer
  { border-color: #68A; background-color: #ACF; }
.inner
  { border-color: #A86; background-color: #FCA;
    margin: 5px; }

In your browser, this is what it looks like:

In a CSS compliant browser, it would display like this:

Whereas in IE, the outer box is reflowed and it comes out like this:

If you've been following closely, you may be wondering why the #navbar stopped short on example with long content. The body had a height of 100% which was too short. Why didn't the box reflow to encompass the content? Apparently the reflowing doesn't work for the body. If the page content is wrapped in a div however and that div's height is set to 100%, it will reflow as expected:

html, body, #body, #navbar { height: 100%; }
html, body { margin: 0; }
#navbar, #content { float: left; }

Resizing to the Container's Height with CSS

Sizing to the height of the container in a CSS compliant browser simply takes advantage of the fact that table cells are the height of the containing row. In some browsers (Firefox, Opera), children of elements with a display of table-cell will render according to the CSS specification. I won't go into the details, but if you are curious, you can see my invalidated bug report. The end effect is that you'll sometimes get strange top margins if you leave the vertical-align property as baseline.

html, body { height: 100%; margin: 0; }
body { display: table; }
#navbar { display: table-cell; vertical-align: top; }

Combining the Two Methods

These two methods are similar enough that they can almost be combined without any CSS hacks. The one issue is that in the IE layout the #navbar is floated. If this is done in the CSS layout the element is pulled out of the normal flow and the height is only that of the contained elements. I personally like to use conditional comments rather than parsing bugs to handle IE specific stuff. Who knows, maybe IE 7 will actually work. Using these, it is painless to combine the two methods. Also, in case you were curious, this still fills the initial screen with less than a screenful of content. (This time I included the whole style tag to show the conditional comments.)

<style type="text/css">
  html, body, #body, #navbar { height: 100%; margin: 0; }
  #body { display: table; }
  #navbar { display: table-cell; }
</style>
<!--[if lt IE 7]>
<style type="text/css">
  #navbar { float: left; }
</style>
<![endif]-->

Applying the Method

The layout that I have been working on converting to CSS needs this method in several places. The HTML looks like:

<div class="layout">
  <!-- top shadow markup omitted -->
  <div class="shadowed">
    <div class="left shadow"></div>
    <div class="body">
      <div class="header"></div>
      <div class="content">
        <div class="leftcol"></div>
        <div class="centercol"></div>
        <div class="rightcol"></div>
      </div>
      <div class="footer"></div>
    </div>
    <div class="right shadow"></div>
  </div>
  <!-- bottom shadow markup omitted -->
</div>

And I want it to render like this:

Header
Navigation
Page Content
Jump Points

There are several columns in this layout whose heights are important:

Though they look very similar, the layout for these two sets of columns is different. I will discuss each in turn.

Drop Shadows

They are often maligned by the web's technorati, but the client wanted drop shadows on this layout. This is a description of how the columns on the center section were done. (The contents of the .shadowed div.) There's really not all that much to them. The bits for specifying the sizes of the shadows are shared by both layouts:

.right { width: 15px; }
.left { width: 10px; }

The CSS version is pretty simple. Though it goes counter to the CSS adherents thinking, all you have to do is look at the layout as if it was in a table:

.layout { display: table; width: 80%; margin: auto; }
.shadow.top, .shadowed, .shadow.bottom
  { display: table-row; }
.left, .body, .right { display: table-cell; }

The IE version is a bit more complicated. You might have noticed that the width of .layout is a percentage. This means it isn't possible to know the actual pixel width. The drop shadows however, do have fixed pixel widths. Most three column layouts float all three columns and either specify all three as percentages or all three as fixed widths. There are a couple different ways to handle this mixed scenario. One would be to float the columns to the left and right and allow the content the fill in the middle. The problem with that is it requires both .left.shadow and .right.shadow to come before .body, and for the CSS layout .right.shadow needs to come after .body.

Instead of using floats, I took advantage of the fact that IE reflows affect absolutely positioned elements as well as floats. So the shadows are just absolutely positioned and there is an appropriate margin on the .body.

#layoutsample { text-align: center; }
.shadowed { height: 0; position: relative; }
.left { position: absolute; left: 0; }
.right { position: absolute; top: 0; right: 0; }
.body { padding: 0 15px 0 10px; }
.shadow.left, .shadow.right { height: 100%; }

Content Columns

The content columns are more a more traditional three column layout (in IE at least). Again, the styles controlling the widths are shared:

.leftcol, .rightcol { width: 25%; }
.centercol { width: 50%; }

The CSS compliant markup is pretty much identical:

.content { display: table; width: 100%; }
.leftcol, .rightcol, .centercol { display: table-cell; }

And the version for IE is pretty simple too:

.content { height: 0; }
.leftcol, .rightcol, .centercol
  { float: left; height: 100%; }

Bugs

I developed this layout working in Firefox 1.5.03 in XP, Fedora Care 4 and OSX. Then I ported it over to Internet Explorer 6.0, Safari 2.0.3 and Konqueror 3.5.2-0.1.fc4. During the porting process I ran across several bugs. Most of them don't warrant mentioning and are noted in comments in the stylesheet, but one affects the HTML. There is a one pixel space under the footer in Firefox XP that doesn't appear in any other browser including Firefox OSX. There's no way I know of to catch that without javascript, so I just whacked a pixel off of all the browsers except IE.

Doing this goes back to the conditional comments which can be used to exclude content from IE as well as include it:

<!--[if !IE]>-->
<style type="text/css">
  .footer { margin-bottom: -1px; }
</style>
<!--<![endif]-->

Samples

The sample above is laid out with this layout. The operative styles are: