September 29, 2004

Two Columns, Quick and Dirty in CSS

I have seen many solutions for creating tableless, two-column designs in CSS, but most of them lack in one way or another. Either they are static width when I’m looking for liquid, or liquid when I need static, and often times if the code isn’t sufficiently tortured, they don’t even achieve the look of the two-column design. You know the look I’m talking about. It’s the one where both columns appear to be the same length. You have one in front of you right now.

How did I do this? First off, let’s work backwards. Assume that I have no solution to the CSS two-column design, and instead let us start with a sensible, semantic structure for our XHTML code. Let us take this logical, flexible and reusable block of code, and see how we can make it work as a two-column design.

Here is the code structure we will work from, free from any style whatsoever. We see all the familiar elements of any basic website: header, footer, content area, and primary navigation. For the most common applications, there’s hardly any more structure you need. How to go about designing it, then?

The first thing I always do when starting a new design is zero my margin and padding styles for every single HTML element, so there is no ambiguity about how the browser should treat spacing. Each browser has its own idea on how much padding to apply to a paragraph tag, how much to use for indenting a list item, and since I am a control freak, I prefer to manually define all my styles. This leaves no room for subjectivity on the part of the browser, and I know that any errors in rendering are my own. So, I always start with the following CSS:

* {

margin: 0;

padding: 0;

}

See the result. Ain’t that beautiful? Every single browser I have tested plays by this rule without any need for hacks.

Next, let’s center a box in our page that will contain all our content. The name I have always used for this outer block is “Whopper”, and the whopper always wraps around all my other elements, including the header, footer, content and navigation areas. You can call it anything you want; something that makes more sense or less sense, depending on your personal coding style.

body {

text-align: center; /* this centers the whopper in IE */

}

div#whopper {

width: 700px;

margin: 20px auto 20px auto; /* this centers the whopper »

in compliant browsers */

text-align: left; /* this returns alignment to normal */

}

Now, according to the W3C’s CSS spec, the only thing we should need to do to center our whopper is to give it a width (so it knows what size element is being centered) and declare “auto” on our left and right margins. However, Internet Explorer ignores auto margins, so this won’t work as a complete solution. Fortunately, Internet Explorer will (wrongly) center block level elements if their containing element declares {text-align: center;}. So that is what we do, and our result is thus (with some color to help differentiate areas).

Now, let’s make our headers and footers take up some space:

div#header {

height: 25px;

background: #9EA671;

}

div#footer {

height: 50px;

background: #9EA671;

}

Check it out. Depending on your browser, you may be witnessing another hang-up, here. As we discussed earlier, Internet Explorer improperly treats the height property like min-height, and will stretch an element if the stuff it contains is bigger than the element itself. Thus, even though we declared a 25 pixel height on our header, the default font size for the h1 tag is taller than 25 pixels, and is pushing out the height of our header. This problem does not affect compliant browsers, as they properly overflow content that doesn’t fit.

The best way that I have found to fix this problem is to simply be disciplined in what you put in boxes with static heights. Make sure you don’t try to jam a giant squid in your refrigerator. This is what we will do:

div#header h1 {

font: bold 15px/1.2em arial, helvetica, verdana, sans-serif;

color: #C0C69F;

text-decoration: none;

}

And this is the result. Next, let’s position our navigation and content areas so they are next to each other:

div#content {

float: right;

width: 500px;

background: #97A199;

}

div#nav {

width: 200px;

float: left;

background: #7F9794;

}

div#footer {

clear: both;

height: 50px;

background: #9EA671;

}

There we are. We have given the navigation area a width of 200 pixels and have floated it to the left. This can cause the content area to do any number of things that we don’t want it to do, like have its text reflow around the navigation area, so we do the same thing to it on the right side. Finally, to keep the footer from trying to bunch itself up in whatever space is available between the navigation and content, we clear both floats so it always flows to the bottom.

Again, we didn’t need any hacks to get this to work in nearly all modern browsers, but I have taken a few preventative steps to avoid potential problems. IE5.2/Mac adheres to the CSS spec very closely when it comes to floats and widths, and will choke if you assign a float to an element without also giving it a width. Most browsers are much more forgiving, but you can save yourself a few headaches by taking the extra step.

Also, flow control seems to be a problem when floating elements in some browsers. Whenever I plan on floating an element to the right, I always make sure that in my HTML code, that element comes before the content that will appear on the left. Too often I will float an element to the right of something, only to have it appear below the content I wanted it to be next to. This is the reason our content element (which floats right) appears before our navigation element (which floats left) in the HTML code for our template.

There are other semantic arguments about why content should appear above navigation in the code flow, or why everything should be absolutely positioned so the code flow has no effect whatsoever on content order, but these considerations are beyond the scope of this tutorial. Again, these are preventative decisions I make when coding a design. Some superstitious people have a lucky horseshoe, others have a lucky rabbit’s foot, and me, I have a lucky code flow.

Now comes the fun part. A major wrankle for many people getting their feet wet in CSS is that it’s impossible to simulate a two (or more) column layout without resorting to tables. I myself was fairly convinced that this was the case, until I reexamined what it was I really needed to accomplish.

Notice how the background on our navigation element falls far short of filling up the column next to the content area. I had always approached the problem by looking at these two areas (in our case, the navigation and content areas) as columns in a table. How can I get this area to be the same height as the one right next to it, even though it doesn’t have enough content to push itself to that height? More dead content? Images that are 6000 pixels tall? Scripts that dynamically write height values? What?

I had all but given up, having decided that this ugliness was just another thing to work around with CSS designs, when it suddenly hit me that I was going at it all wrong. The solution had nothing to do with the elements in question, but with their containing element. The containing element runs from corner to corner of the entire area I want to columnize. If I could somehow give it a background that looked exactly how I want my columns to appear… Eureka.

Let us then use our containing element, the Whopper, to create our two column design. We will need to create one image which will serve as the repeating background for the entire whopper area. Mine is 700 pixels wide, 10 pixels tall, and looks exactly as you would expect. We write the following code:

div#whopper {

background: #C0C69F url(dirty_whopper_bg_01.gif) top left repeat-y;

width: 700px;

margin: 20px auto 20px auto;

text-align: left;

}

div#content {

float: right;

width: 500px;

/* background: #97A199; */ /* no longer necessary */

}

div#nav {

width: 200px;

float: left;

/* background: #7F9794;*/ /* no longer necessary */

}

Whammo. Two columns that automatically fill up the dead space with color.

Now that we're done with the hard stuff, we can actually start to have some fun with this design. Fun will have to wait for another time, however, as this designer is plum tuckered after racing the neighborhood kids all evening on Big Wheels and girlie bikes.