In Search of the Holy Grail
Issue № 211

In Search of the Holy Grail

I’m sorry. Really. I didn’t name it. I don’t mean to overstate its importance or trivialize the other and rather weightier Holy Grails.

Article Continues Below

But the name’s out there, and we all know what it means.

Three columns. One fixed-width sidebar for your navigation, another for, say, your Google Ads or your Flickr photos—and, as in a fancy truffle, a liquid center for the real substance. Its wide applicability in this golden age of blogging, along with its considerable difficulty, is what has earned the layout the title of Holy Grail.

Many articles have been written about the grail, and several good templates exist. However, all the existing solutions involve sacrifices: proper source order, full-width footers, and lean markup are often compromised in the pursuit of this elusive layout.

A recent project has brought my personal grail quest to an end. The technique I’ll describe will allow you to deploy the Holy Grail layout without compromising your code or your flexibility. It will:

  1. have a fluid center with fixed width sidebars,
  2. allow the center column to appear first in the source,
  3. allow any column to be the tallest,
  4. require only a single extra div of markup, and
  5. require very simple CSS, with minimal hacks patches.

On the shoulders of giants#section2

The technique presented here is inspired by Alex Robinson’s brilliant One True Layout. Alex even addressed the Holy Grail problem in his article, but his solution requires two wrappers and makes padding difficult without a further layer of divs within each column.

Another lead came from Eric Meyer’s adaptation that uses positioning to mix multiple unit types. His example also yields a three-column layout with fixed sidebars and a liquid center. Unfortunately, it relies on approximate percentages and fills a portion of the viewport that varies widely with different screen resolutions.

Enough talk—let’s see some code#section3

The required HTML is intuitive and elegant.

(For the sake of clarity in demonstrating this technique, we are intentionally using the non-semantic ids “center,” “left,” and “right.” We recommend you use semantic ids in any application of this technique. —Ed.)

<div id="header"></div><div id="container">
  <div id="center" class="column"></div>
  <div id="left" class="column"></div>
  <div id="right" class="column"></div></div><div id="footer"></div>

That’s it. A single extra div to contain the columns is all that you need; this satisfies even my obsessive compulsive markup habits.

The stylesheet is almost as simple. Let’s say you want to have a left column with a fixed width of 200 pixels and a right column with a fixed width of 150 pixels. To simplify the comments, I’ll abbreviate the left, right, and center columns as LC, RC, and CC, respectively. The essential CSS is here:

body {
  min-width: 550px;      /* 2x LC width + RC width */
}
#container {
  padding-left: 200px;   /* LC width */
  padding-right: 150px;  /* RC width */
}
#container .column {
  position: relative;
  float: left;
}
#center {
  width: 100%;
}
#left {
  width: 200px;          /* LC width */
  right: 200px;          /* LC width */
  margin-left: -100%;
}
#right {
  width: 150px;          /* RC width */
  margin-right: -150px;  /* RC width */
}
#footer {
  clear: both;
}
/*** IE6 Fix ***/
* html #left {
  left: 150px;           /* RC width */
}

Simply replace the values with your desired dimensions and the grail is yours. The technique works in all modern browsers: Safari, Opera, Firefox, and (with the single-rule hack at the bottom) IE6. IE5.5 support would require at least a box-model hack, which is left as an exercise to the reader.

Take a look and marvel at the elegance.

How it works#section4

The strategy is straightforward. The container div will have a liquid center and fixed-width padding on the side. The trick is then to get the left column to line up with the left padding and the right column with the right padding, leaving the center column to fill the liquid width of the container.

Let’s build this up step by step.

Step 1: Create the frame#section5

Start with the header, footer, and container.

<div id="header"></div><div id="container"></div><div id="footer"></div>

We pad the container with the width we want our left and right columns to occupy.

#container {
  padding-left: 200px;   /* LC width */
  padding-right: 150px;  /* RC width */
}

Our layout now looks like this:

Figure 1: the outline of the header, footer, and container

Step 1: Create the frame

Step 2: Add the columns#section6

Now that we have our basic frame, we’ll stick in the columns.

<div id="header"></div><div id="container">
  <div id="center" class="column"></div>
  <div id="left" class="column"></div>
  <div id="right" class="column"></div>
</div><div id="footer"></div>

Next we add the appropriate widths and float them to get them in line. We’ll also need to clear the footer to keep it beneath the floated columns.

#container .column {
  float: left;
}
#center {
  width: 100%;
}
#left {
  width: 200px;  /* LC width */
}
#right {
  width: 150px;  /* RC width */
}
#footer {
  clear: both;
}

Note that the 100% width on the center column refers to the width of the container div, exclusive of the padding. We’ll see this 100% width again as the layout comes together, and it will still refer to this central width of the container.

The columns now want to line up in order, but because the center column is taking up 100% of the available space, the left and right columns wrap.

Figure 2: the three columns lined up in source order

Step 2: Add the columns

Step 3: Pull the left column into place#section7

The only thing left is to get the colums to line up with the padding on the container. The center column starts exactly where it needs to be, so we’ll focus on the left column.

It takes two steps to get the left column in place. First, we’ll pull it all the way across the center column with a 100% negative margin. Remember that the 100% refers to the central width of the container, which is also exactly the width of the center column.

#left {
  width: 200px;        /* LC width */
  margin-left: -100%;  
}

Now the left column is overlapping the center column, sharing its left edge. The right column floats left and nestles against right edge of the center column (but still wraps), leaving us with the following:

Figure 3: the left column pulled 100% to the left

Step 3: Pull the left column into place—halfway there

To push the left column the rest of the way over, we’ll use relative positioning with an offset that’s exactly the width of the left column.

#container .columns {
  float: left;
  position: relative;
}
#left {
  width: 200px;        /* LC width */
  margin-left: -100%;  
  right: 200px;        /* LC width */
}

The right property pushes it 200px away from the right edge; that is, to the left. Now the left column lines up perfectly with the left padding of the container.

Figure 4: the left column offset 200px to the left

Step 3: Left column pulled into place

Step 4: Pull the right column into place#section8

The only task remaining is to pull the right column into place. To do that, we just need to pull it out of the container and into the container’s padding. We’ll again use a negative margin.

#right {
  width: 150px;          /* RC width */
  margin-right: -150px;  /* RC width */
}

Everything is now in its right place, and the wrapping disappears.

Figure 5: the right column pulled 100% to the right

Step 4: Pull the right column into place

Step 5: Design defensively#section9

If the browser window is resized so that the center becomes smaller than the left column, the layout breaks in a standards-compliant browser. Setting a min-width on the body keeps your columns in place. With IE6 this doesn’t happen, so the fact that it doesn’t support min-width isn’t a problem.

body {
  min-width: 550px;  /* 2x LC width + RC width */
}

Of course, no layout technique would be complete without requiring some sort of workaround in Internet Explorer. The negative margin pulls the left column too far to the left in IE6 (the full width of the browser window). We need to push it back to the right the full width of the right column—using the star-html hack to mask it from other browsers—and we’re ready to go.

* html #left {
  left: 150px;  /* RC width */
}

The reason we need to use the width of the right column involves a bit of algebra. I won’t bore you with the details; you can work it out for yourself or just consider it another one of IE’s many charms.

Padding, please#section10

I’m no designer, but looking at the layout above offends even my aesthetic sensibilities. The unpadded columns are hard on the eyes and difficult to read. We need whitespace.

One of the drawbacks of using percentages with the One True Layout to create liquid columns that it makes padding the columns a bit tricky. Percentage paddings tend to look bad at some screen widths. Fixed paddings can be added, but only by cluttering the markup with a div nested inside each column.

With this technique, padding isn’t a problem. Padding can be added directly to the left and right columns; just adjust the width accordingly. To give a 10-pixel padding to the left column in the example above, but keep it’s full width (padding + width) at 200px, simply change the rule as follows:

#left {
  width: 180px;        /* LC fullwidth - padding */
  padding: 0 10px;
  right: 200px;        /* LC fullwidth */
  margin-left: -100%;
}

Padding the center requires a little more ingenuity, but no more markup and only a pinch of additional CSS.

The padding plus a width of 100% causes the center column to expand beyond the non-padded width of the container. In order to tame it back into place, we need to increase the right margin by the total amount of the padding. This ensures that the center column is only as large as we expect it to be.

Also, since the center column is now wider, the left column has a larger distance to move in order to get to the correct place. Increasing the offset by the total center padding does the trick.

To make this concrete, I’ll modify the example to add a 10-pixel padding to each side column (for a total of 20 pixels), and a 20-pixel padding to each side of the center (for a total of 40 pixels). The new CSS looks like this:

body {
  min-width: 630px;      /* 2x (LC fullwidth +
                            CC padding) + RC fullwidth */
}
#container {
  padding-left: 200px;   /* LC fullwidth */
  padding-right: 190px;  /* RC fullwidth + CC padding */
}
#container .column {
  position: relative;
  float: left;
}
#center {
  padding: 10px 20px;    /* CC padding */
  width: 100%;
}
#left {
  width: 180px;          /* LC width */
  padding: 0 10px;       /* LC padding */
  right: 240px;          /* LC fullwidth + CC padding */
  margin-left: -100%;
}
#right {
  width: 130px;          /* RC width */
  padding: 0 10px;       /* RC padding */
  margin-right: -190px;  /* RC fullwidth + CC padding */
}
#footer {
  clear: both;
}/*** IE Fix ***/
* html #left {
  left: 150px;           /* RC fullwidth */
}

Of course, top and bottom padding can be added without any problems.  See this nicely padded version for the full template.

This technique works just as well for ems. Unfortunately, you can’t mix-and-match ems and pixels, so choose, but choose wisely.

Equal-height columns#section11

This technique really comes together when the columns are given equal heights. The method I’m using is adapted wholesale from the One True Layout, so I won’t go over it in detail. To deploy it, simply add the following CSS:

#container {
  overflow: hidden;
}
#container .column {
  padding-bottom: 20010px;  /* X + padding-bottom */
  margin-bottom: -20000px;  /* X */
}
#footer {
  position: relative;
}

Here, I’ve given the columns an extra padding of 10px on the bottom.

The usual caveats apply. Be aware that Opera 8 has a bug with overflow: hidden that leaves all of your columns huge. A workaround is detailed on the One True Layout page; you can use that, or wait for Opera 9 (which fixes the bug) to come out of beta.

One additional problem unique to this layout is that IE doesn’t clip the column backgrounds at the bottom of the container. They spill out over the footer if the page is not as tall as the viewport. This isn’t a problem if you don’t have a separate footer, or if your pages are tall enough to ensure that you’ll always take up the whole viewport.

If you need that footer, though, never fear. This, too, is fixable, but requires one more div. Add a wrapper to the footer, like so:

<div id="footer-wrapper">
  <div id="footer"></div>
</div>

Now re-use the same trick with the equal columns to make the footer wrapper extend beyond the page, leaving the footer itself for you to do with as you please.

* html body {
  overflow: hidden;
}
* html #footer-wrapper {
  float: left;
  position: relative;
  width: 100%;
  padding-bottom: 10010px;
  margin-bottom: -10000px;
  background: #fff;         /* Same as body 
                               background */
}

This solves the problem, and leaves us with the desired result and a minimum of cruft.

Oh, and there’s one more thing#section12

The extremists out there might be wondering to themselves whether there’s not an even better way to do this. After all, the method I’ve illustrated introduces a non-semantic container div. Surely we can’t have one extra div cluttering up our otherwise flawless markup.

If you, like me, have been wondering this very thing, wonder no more. As a special bonus, I present to you the wrapper-free Holy Grail, in all its minimalist glory. One div for each section of the code—no more, no less. Semantic bliss, almost worthy of the title of “Holy Grail.”

The principle behind the CSS is the same. The padding has been applied directly to the body, eliminating the need for any containers. Negative margins stretch the header and footer to ensure that they take up the entire space.

This tiny version works in all the aforementioned browsers, even (almost shockingly) Internet Explorer. Equal-height columns do not work, however, and this layout will break down for very small window widths. Use it with caution.

So what now?#section13

While this particular application of the Holy Grail is rather specific, the technique can be generalized considerably. Why not have two liquid columns? Why not switch up the column orders? These applications are beyond the scope of this article, but achievable with minor modifications. Use the grail wisely, and it can be a particularly handy (and clutter-free) addition to your bag of CSS tricks.

About the Author

Matthew Levine

Before working as a full-time web developer, Matthew taught elementary school and studied physics and philosophy. When he isn't streamlining websites he spends his time in the bayous of South Louisiana perfecting his cajun two-step.

227 Reader Comments

  1. I need to place horizontal navigation in the #Header. However, as this contains ‘float’ it causes the #container to collapse for some reason. I have tries to ‘clear:both’, but nothing seems to work. I am reasonably new at css and am really struggling.

    Can anyone explain why?

    My code is as the original example, with this added menu…

    The added css…

    #navbar {
    float: left;
    margin: 0;
    padding-top: 5px;
    list-style: none;
    background: #5DF4C5;
    }

    #navbar li {
    float: left;
    margin:0;
    padding:0;
    font-family: Verdana, Helvetica, Arial, sans-serif;
    font-size: 1em; }

    #navbar a {
    float:left;
    display: block;
    margin: 0 2px 0 0;
    padding: 4px 20px;
    color: #369;
    text-decoration: none;
    border-bottom: none;
    background: #5DF4C5;
    }

    #navbar li a.current_page_item {
    color: #369;
    background: #fff;
    font-weight: bold; }

    #navbar li a:hover.current_page_item {
    background: #fff;
    color: #369;
    }

    #navbar li a:hover {
    background: #369;
    color: #fff;
    font-weight: bold; }

  2. I have a problem with IE (in FF all works superb). In IE left columns after load is disappear till I move mouse over right column. After them all works without problems. Can someone tell me why?

    Here is link do this page:

    http://www.dywiz.com/test/arbitraz/

    thanks in advance.

  3. First off: love this article. It helped me tremendously. One problem I can’t seem to fix is that when resizing my browserwindow in IE6.0.29 (using the middle top-right browserbutton), the left column ‘forgets’ the margin and is placed to the right of the container area. You can find an example on http://www.niios.nl/test/testpage.php
    I really hope you can help me with this.
    Kind regards,

    Vivienne

  4. As always a great article. Of course with a new Windows OS (Vista) comes a new IE with new problems, works great on Firefox but on IE 7+ the left column is totally off the page, hidden way left.

  5. Great article, it has helped me a lot.

    As Max Fraser mentioned, the left column has disappeared in IE 7. Just wondering if anyone knows a work around yet?

    Cheers

  6. The holy grail yes, it may be mighty, but it has a little bit of a problem when using anchor tags. I found out this problem while working on a project at work. So I created a test page for you users to check out and see if you can come up with a solution.

    http://www.arnoldeqp.com/ala.htm

    Travel to the link above, and click on one of the 3 list items in the main part of the page. They read Exclusive Listing, Consignment, and Outright Purchase. If you click on one of those links it will shoot you to that area below on the page using an anchor tag. What happens in the might holy grail is that you get stuck and can seem to get back to the top of the page. It displays everything in the source code, but just chops off the top.

    If any of you have a solution for this might problem I would love to hear from you.

  7. I just came back to laugh at all the people who still believe CSS DIVS are a great way to layout a site. As usual, a new browser release breaks the ‘hacks’ used to force the layout to work.

    While you guys tear your hair out trying to get a simple 3 column expanding layout to work in CSS DIV, I will use tables and feel very relaxed and happy.

    TABLES ARENT BROKEN so i wont fix them.

  8. This is a great attempt at a lean three column layout. However, It doesn’t work at all in IE 5.2 for mac.

  9. Hi there,

    I have been using this design now for a few websites, and I have been very happy with it (I did adapt it by putting conditional comments instead of hacks)

    I would think it would be very helpful to update the article with the new corrections (including the left column disappearing issue) and, most importantly, using conditional comments instead of hacks.

    I don’t understand why hacks are still being used!!

    Also, while I’m at it, I can point you to a weird issue in IE7:
    “www.nuiregister.com”:http://www.nuiregister.com/

    For some reason the footer shifts up to the top of the page.
    On top of this the page has a huge amount of white space at the end.

    I think working in IE7 corrections is going to be very very important what with it’s impending release and Automatic Update distribution.

  10. I have the holy grail implemented in a more complicated site here:
    “www.invisibleagent.com”:http://www.invisibleagent.com

    In this case, for IE7, I have temporarily used conditional comments to set #footer-wrapper to display:none

    Other than this footer issue and the extremely long page(?!), everything else is hunky dory.

    Cheers for all the hard work.

    Alex

  11. IE 7 seems not to care about the margin/padding-bottom “trick”: the page looks 1 mile long.
    Giving “position:relative” to the div #container solves the problem.

    Ciao,
    Andrea

  12. I’ve put in the min-width fix, but my right column still shifts when the center column gets shorter than the two other columns. Any thoughts? Please excuse the mess at the site right now http://www.murmp.com still just testing stuff out.

    The structure/layout code is at http://www.murmp.com/css/proper.css

    FYI, I am using conditional source code output (still fine tuning that as well, but most new FF gets “correct” code), so if it isn’t right, then let me know and I’ll just hard code to always give the “proper” source.

  13. There’s a much simpler way to fix the background-not-clipping problem in IE. I demonstrate:

    #footer { background-color: #66FF66 ! IMPORTANT; }

    There you go. Instead of adding an extra DIV and a whole bunch of star-hacked CSS, just use the !IMPORTANT modifier to force IE to do your bidding.

    I’ve only tested this in IE 6, ’cause that’s all I have handy at the moment. Can anybody take a look at other versions of IE and see if they behave nicely when spoken to firmly?

  14. Is there any way to accomplish when the divs are ordered:
    left, right, center?

    Your example currently goes:
    center, left, right

    This currently doesn’t work semantically for my design since it would display the content in an incorrect order if you’re using a text base browser.

  15. I just implemented this – I had been looking for a decent solution to this problem for a while having implemented my own 3 column/center fluid design to replace a table layout. I could never get a image to be fixed at bottom right and so have left my old table powered layout in place for a number of years.

    I’ve just solved this by doubling up on the container and 3 cols B4 the footer (obviously giving them new IDs so as not to be unstandard). Placing the image in the right column.

    It works a treat.

    thank you!!!

  16. This is a great article and something that has enabled me to get my menu working in ie6 (see weeteevee’s site).

    Up to last night I just used a standard 3 div left/middle/right layout. When I put the menu into the left pane it refused to work in ie6. I cried, I wept, I discovered that if the menu div is in the content after the div that it will be layered over when expanded then the menu will work. So I remembered bookmarking this in delicious and here I am and there is works (I’ve applied the disappearing left column fix and am ploughing through the rest of the posts.

    However, I have got to say, the amount of grief that is involved with all of this when we have the simplicity of tables at our disposal is enough to make me want to scream. Afterall did people drop their bicycles while cars were being perfected? No they did not. Css / standardisation is a long way off and realistically at a basic layout level tables do everything you need without the fuss or 23 pages of forum responses.

    Thanks for the article, and as message to the haters….keep whining it serves as a reason to go make more tea.

  17. The holy grail, much like all complex css based sites does not work. The left hand column behaves like a ginger kid at a playground and spends most of its time off the screen. In the end I had to float the center 3 panes and remove their position: relative in order for the vertical flyout menu to work correctly. Basing a whole industry on something this complex and broken (css) is insane. We should all be rethinking our use of css.

  18. Many months ago I was looking for a multi-column CSS approach and found this article. I implemented it in one of my personal sites but I have noticed that while it displays almost fine in Firefox 2.0 it doesn´t do well in IE6.

    In IE6 the three column layout is kind of broken, the left and right columns show fine. But the middle column instead is flushed down so that the top side is not aligned with the top of the other two columns.

    Can anybody tell me whether this was intended and perhaps what is wrong with it?

  19. Has anyone got this layout working in netscape 8.12? not my browser of choice, but I’m just curious…

    I seem to be having problems with the page extending way beyond the actual content (especially evident when you re-size the browser window to approx 800×600 or less). Also, I have noticed that occasionally the copy in the left/right columns cuts off when it reaches the footer – ideas anyone?

    Many thanks!

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA

I am a creative.

A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
Career