Developer

Getting equal-height columns in a three-column layout

One of the challenges of working with a multiple-column layout is how to get background colors in each of the columns to fill the full column height. Here's our solution to this problem.

This article originally appeared in the Design & Usability Tactics newsletter. Click here to subscribe automatically.

Many Web sites use some variation of a basic three-column page layout. A typical layout often includes a full-width header at the top of the page, then the three-column section, which is composed of a center column for the main content, with side columns for navigation links and sidebar items. A full-width footer at the bottom of the page rounds out the basic layout.

My article, "Use CSS floats to create a three-column page layout," describes a basic technique for creating a three-column page layout. And I offer some refinements to the technique in last week's article, "Raise text relevance by rearranging a three-column layout."

One of the challenges of working with a multiple-column layout is how to get background colors in each of the columns to fill the full column height. It's not a problem in page designs where all the columns have the same background color, but creating separate colors for each column is more problematic.

The trouble is that divs expand vertically just enough to accommodate their contents. So, the obvious tactic of assigning a background color to the div, corresponding to a page column, doesn't have the desired effect of filling the full column height with that color. Instead, the color stops at the end of the div's content.

Using a wrapper div to create a background color for the shorter column (see last week's article) is an effective solution when you can predict which column will be the longest; but creating a three-column layout that can accommodate different combinations of column lengths requires a different approach.

Start with a three-column liquid layout

The basic technique for creating a three-column liquid layout with CSS is to use floats to push the outer columns out to the left and right sides of the browser window and allow the main content to flow up into the gap in the middle, between the outer columns. Setting the left and right margins for the middle column confines the main content to a nice vertical column instead of spreading out into the side columns. The side columns are each a fixed-width, but the center column is free to expand and contract with changes in the browser window, making this a liquid layout. (This technique is described in more detail in the previous column.)

Here's the example code for a three-column layout with full-width header and footer, as shown in Figure A.

First, the CSS styles:

body {
    margin: 0px;
    padding: 0px;
}
div#header {
    text-align: center;
    background-color: #CCCCCC;
    height: 60px;
    margin: 0px;
    padding: 1px;
}
div#navcol {
    padding: 10px;
    width: 130px;
    float: left;
}
div#main {
    padding: 10px;
    margin-left: 160px;
    margin-right: 160px;
}
div#sidecol {
    padding: 10px;
    width: 130px;
    float: right;
}
div#foot {
    border-top: solid #000 1px;
    background-color: #CCCCCC;
    padding: 10px;
    text-align: center;
    clear: both;
}

And, here's the XHTML markup. (For the sake of brevity, I show the contents of both side column divs as simple unordered lists, and abbreviate the main content and footer text.)

<body>
<div id="header">
    <h1>Header Text</h1>
</div>
<div id="navcol">
    <h4>Nav Column</h4>
    <ul>
        <li>Let me not to the marriage of true minds</li>
        <li>Admit impediments; love is not love</li>
        <li>Which alters when it alteration finds</li>
        <li>Or bends with the remover to remove</li>
        <li>Oh, no, it is an ever fixed mark</li>
        <li>Let me not to the marriage of true minds</li>
    </ul>
</div>
<div id="sidecol">
    <h4>Starboard Side Column</h4>
    <ul>
        <li>Let me not to the marriage of true minds</li>
        <li>Admit impediments; love is not love</li>
        <li>Which alters when it alteration finds</li>
        <li>Or bends with the remover to remove</li>
    </ul>
</div>
<div id="main">
    <h2>Main Content</h2>
    <p> That looks on tempests ... taken.</p>
    <p>That looks on tempests ... taken.</p>
    <p>If this be error ... height be taken.</p>
    <p>That looks on tempests ... remove.</p>
</div>
<div id="foot">
    <p>Footer text goes here. ... </p>
</div>
</body>

Figure B shows the result of adding background colors to each of the column divs (navcol, sidecol, main). However, this isn't the effect we're looking for.

Adding column backgrounds

The solution is to add a couple of divs as containers for the column content divs, and use these containers to create the column backgrounds. One div (column2) creates two columns with a combination of a background image and a background color; the other div (column1) creates the third column with another background image. The background images are small GIF files, one-pixel tall by whatever width you need to create the column width (in this case, it's 150 pixels). The column content divs have transparent backgrounds and are formatted to position the column text over the corresponding background color or image.

To style the new divs, we'll add the following styles to the CSS stylesheet:

div#column2 {
    margin: 0;
    padding: 0;
    background-image: url(side2.gif);
    background-position: right;
    background-repeat: repeat-y;
    width: 100%;
    background-color: #FFFF99;
    
}
div#column1 {
    margin: 0px;
    padding: 0px;
    background-image: url(side1.gif);
    background-repeat: repeat-y;
    width: 100%;
    
}

To ensure that the background divs fully enclose the floated column content, we'll need to add a clearing element to the code and the following style to the stylesheet. This style employs a class selector instead of an id to facilitate easy reuse throughout the site.

.clear {
    clear: both;
    display: block;
    height: 1px;
    overflow: hidden;
    margin: 0;
    padding: 0;
}

Here's the XHTML markup with the added divs:

<body>
<div id="header">
    <h1>Header Text</h1>
</div>
<div id="column2">
    <div id="column1">
        <div id="navcol">
            <h4>Nav Column</h4>
            <ul>
                <li>Let me not to the marriage of true minds</li>
                <li>Admit impediments; love is not love</li>
                <li>Which alters when it alteration finds</li>
                <li>Or bends with the remover to remove</li>
                <li>Oh, no, it is an ever fixed mark</li>
                <li>Let me not to the marriage of true minds</li>
            </ul>
        </div>
        <div id="sidecol">
            <h4>Starboard Side Column</h4>
            <ul>
                <li>Let me not to the marriage of true minds</li>
                <li>Admit impediments; love is not love</li>
                <li>Which alters when it alteration finds</li>
                <li>Or bends with the remover to remove</li>
            </ul>
        </div>
        <div id="main">
            <h2>Main Content</h2>
            <p> That looks on tempests ... taken.</p>
            <p>That looks on tempests ... taken.</p>
            <p>If this be error ... height be taken.</p>
            <p>That looks on tempests ... remove.</p>
        </div>
        <div class="clear">&nbsp;</div>
    </div>
</div>
<div id="foot">
    <p>Footer text goes here. ... </p>
</div>
</body>

The result is shown in Figure C.

Deconstructing the code

There are two wrapper divs surrounding the column content divs because we need to place background images in two different locations. The div#column2 style creates the middle and right columns, the background-color: #FFFF99; rule defines the middle column color, and the background image (background-image: side2.gif;) creates the right column. The background image is positioned to the right side of the div (background-position: right;) and repeated vertically (background-repeat: repeat-y;). The div is floated (float: left;), so it follows the same positioning rules as the floated content divs.

Similarly, the div#column1 style creates the left column with another background image (background-image: side1.gif;). It's positioned on the left side of the div (background-position: left;) and repeated vertically (background-repeat: repeat-y;). There is no background color in this div, so the background from div#column2 shows through.

The width attribute (width: 150px;) for both outer column content divs (div#navcol and div#sidecol) exactly matches the width of the background images for the corresponding columns. In this case, both of the outer columns are the same width, but they could be different if that's what your page layout requires.

One of the secrets to this technique is the clearing element (<div class="clear">&nbsp;</div>) that follows the main content div. This is what pulls the background divs down to enclose the bottom of the longest column. The clearing element is just a div containing a nonbreaking space character. It follows the last nonfloated element in the group of elements to be enclosed by the surrounding divs—in this case, the main content div. The .clear class style includes the clear: both; rule, which forces it to flow into a position where it's clear of floated elements on both sides. This effectively pushes the clearing element down to the bottom of the columns.

The background divs must expand down the page to include the clearing element, thus ensuring that the background colors and images extend the full height of the page columns. The rest of the .clear style attributes make sure it's treated as a block element and suppress its display. (Note: The height is set to 1px rather than zero because Netscape ignores the element if its height is 0px.)

Using this technique, you can create a three-column layout with background colors that extend the full height of the columns—no matter which column is longer. Figure D and Figure E show how the page adjusts automatically to accommodate variations in column lengths.

Editor's Picks

Free Newsletters, In your Inbox