Intermediate CSS Techniques

CSS Columns Done Right: 2-Column Layouts

As you read through these case studies have a text editor open and code along with me. Check your results in a browser. DON'T just copy and paste my code; for better retention you should actually WRITE your own code.

Two-column layouts split a section of the main page vertically, separating content:

A lot of 2-column layouts are easy to code with just two XHTML elements, in this case div id="linklist and div id="page_copy":

	<div id="linklist"> 
		<ul>
			<li><a href="#">Home</a></li>
			<li><a href="#">Hosting</a></li>
			<li><a href="#">Support</a></li>
			<li><a href="#">Consulting</a></li>
			<li><a href="#">Installation</a></li>
			<li><a href="#">Integrations</a></li>
			<li><a href="#">Courseware</a></li>
			<li><a href="#">Themes</a></li>
			<li><a href="#">Training</a></li>
		</ul>
		  
		<ul>
			<li><a href="#">Partners</a></li>
			<li><a href="#">Jobs</a></li>
			<li><a href="#">Development</a></li>
			<li><a href="#">Headquarters</a></li>
			<li><a href="#">Helpdesk</a></li>
		</ul>
		<q>"Moodle is a real gift to forward thinking educators."</q>
	</div>

	<div id="page_copy">
		<h1>Welcome to the Moodle Service Network!</h1>

		<p>Moodle is a course management system designed to help educators who want to create quality online courses. The software is used all over the world by universities, schools, companies and independent teachers. Moodle is open source and completely free to use.</p>
		
		<p>The Moodle Partners are a group of service companies guided by the core developers of Moodle. We provide a range of optional commercial services for Moodle users, including fully-serviced Moodle hosting, remote support contracts, custom code development and consulting. Our customers range from individual educators up to company training departments and universities. </p>
		
		<p>Please explore our range of friendly support services using the links on the left, and help Moodle support you by supporting the developers!</p>
	</div>
	

Float Columns

To make these two-columns we can float the two divs, giving each a width:

		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 20%; background: #FFD991; }
		div#page_copy { width: 80%; background: #FFEECE; }
	

This yields the following:

There are two problems:

  1. By using percentages, our page_copy div might run to far to the right
  2. We can't rely on either column to run an equal height to the other column

Let's tackle problem 1 first.

Problem 1

By using percentages, our page_copy div might run to far to the right. This is easy enough to fix, right? Simply use fixed width columns:

		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 200px; background: #FFD991; }
		div#page_copy { width: 600px; background: #FFEECE; }
	

Size Matters

With floated fixed-width designs, we must hope the user has a browser with a resolution equal to or higher than our total width, in this case 800px. Otherwise this happens:

Since my total width is 800px, this will always happen with windows smaller than 800px wide.

Solution 1

Aside from absolutely positioning both elements, there is at least one solution: to force these elements to not wrap, add a parent div...

	<div id="page">

		<div id="linklist"> 
			<ul>
				<li><a href="#">Home</a></li>
				<li><a href="#">Hosting</a></li>
				<li><a href="#">Support</a></li>
				<li><a href="#">Consulting</a></li>
				<li><a href="#">Installation</a></li>
				<li><a href="#">Integrations</a></li>
				<li><a href="#">Courseware</a></li>
				<li><a href="#">Themes</a></li>
				<li><a href="#">Training</a></li>
			</ul>
			  
			<ul>
				<li><a href="#">Partners</a></li>
				<li><a href="#">Jobs</a></li>
				<li><a href="#">Development</a></li>
				<li><a href="#">Headquarters</a></li>
				<li><a href="#">Helpdesk</a></li>
			</ul>
			<q>"Moodle is a real gift to forward thinking educators."</q>
		</div>
		
		<div id="page_copy">
			<h1>Welcome to the Moodle Service Network!</h1>
		
			<p>Moodle is a course management system designed to help educators who want to create quality online courses. The software is used all over the world by universities, schools, companies and independent teachers. Moodle is open source and completely free to use.</p>
			
			<p>The Moodle Partners are a group of service companies guided by the core developers of Moodle. We provide a range of optional commercial services for Moodle users, including fully-serviced Moodle hosting, remote support contracts, custom code development and consulting. Our customers range from individual educators up to company training departments and universities. </p>
			
			<p>Please explore our range of friendly support services using the links on the left, and help Moodle support you by supporting the developers!</p>
		</div>
		
	</div>
	

...with a fixed width of 800px (200+600):

		div#page { width: 800px }
		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 200px; background: #FFD991; }
		div#page_copy {width: 600px; background: #FFEECE; }
	

Which gives us this result at wide widths:

...and this result,at narrower widths:

The only problem here--and this is a problem that fixed widths are usually stuck with--is the fact that lower width have to use the scroll bar to see critical content. The best way to avoid this is to use a total width that is less than your audience's standard screen resolution, so perhaps 700px total width instead of 800 to make up for those on 800x600 screen displays.

Alternative Solution: Elastic Widths

An alternative is to use percentage-based widths with the max-width property, like so:

		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 20%; max-width: 15em; background: #FFD991; }
		div#page_copy { width: 80%; max-width: 35em; background: #FFEECE; }
	

The initial widths ensure that low-width browsers resize the divs' widths relative to the window width. The max-width property--which only works in more standards-compliant browsers like Firefox--ensures that the divs STOP resizing at a certain point, in this case a width specified in ems.

Problem 2

We can't rely on either column to run equal height to to other column. This is a sticky one. We're inclined to want to use the height property. We don't want to use px because we don't know how many px the content of either column is going to be. So let's try percentages:

		div#linklist, div#page_copy { float: right; }
		div#linklist { width: 20%; max-width: 15em; height: 100%; background: #FFD991; }
		div#page_copy { width: 80%; max-width: 35em; height: 100%; background: #FFEECE; }
	

This works great in Firefox and other standards-compliant browsers until your text starts to go beyond 100%, that is, beyond the height of the window!

So height doesn't really solve it here. As I see it, there are only two really viable solutions:

  1. Faux Columns (Dan Cederholm)
  2. Equal Height aka Padding/Margin Height Hack (Alex Robinson)

Disclaimer

Before I show you these, let me say that both of these are hacks to get the job done. For the performance we really need, we'd have to use the CSS value table in our display property, but again, that's a solution only for more standards-compliant browsers, like some versions of Firefox.

It won't be until CSS 3 that we really see a good, solid CSS-based solution to this problem. This is absolutely the only time you will ever hear me say, "This is one thing table-based layouts did better than CSS."/p>

HOWEVER, the solutions I'm about to show you are still very useable, and the benefits of a tableless design outweigh the hack-factor.

Faux Columns (Dan Cederholm)

The Faux Columns technique works with fixed-width columns, such as...

		div#page { width: 800px }
		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 200px; background: #FFD991; }
		div#page_copy {width: 600px; background: #FFEECE; }
	

The trick is quite simple and starts with a new image that looks like this:

The gif contains only two colors, and is 800px wide by 1px high.

What Would You Do?

What do you think I'm going to do with this new image? How would you try to get the effect we need with CSS? Take a few minutes to think about this before moving on!

Here's what I do with it in the CSS:

		body { 
			background: url("images/2col_bg.gif") repeat-y; 
			padding: 0; 
			margin: 0; 
			}
		div#page { width: 800px; }
		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 200px; }
		div#page_copy { width: 600px; }
	

Notice we got rid of the background properties for both divs, and just added a background image, our 1px high background, to the body element. We told it to only repeat on the y-axis. We also told the body to not apply any padding or margin at this point. Here's the result:

Nice!

Now let's look at another possible solution.

Equal Height aka Padding/Margin Height Hack (Alex Robinson)

Alex Robinson calls this the Equal Height technique. I call it the Padding/Margin Height Hack. While I don't like the false manipulations that have to happen to make this work, I do like that this works with flexible and elastic layouts, such as:

		div#linklist, div#page_copy { float: left; }
		div#linklist { width: 20%; max-width: 15em; background: #FFD991; }
		div#page_copy { width: 80%; max-width: 35em; background: #FFEECE; }
	

The first step is to make sure that the container element, div#page, cuts off any overflow of content:

		div#page { 
			overflow: hidden 
		}
	

Now to apply fake padding and fake margins...

What Would You Do?
  1. Where is the padding going to go? Where is the margin going to go?
  2. What values would you use?

We're going to trick the browser into thinking that columns are as tall as the content that any given column may contain by adding an enormous amount of padding on the bottom of each. This forces the height of all columns way past the bottom of the screen.

		div#linklist, div#page_copy { 
			float: left; 
			padding-bottom: 32767px; 
		}
	

But that's still not right. So now add a negative margin on the bottom of each column to bring the flow of the document back to the same point where the padding-bottom began:

		div#linklist, div#page_copy { 
			float: left; 
			padding-bottom: 32767px; 
			margin-bottom: -32767px;
		}
	

It sounds like we've just defeated the purpose by using the exact same negative value to counter the positive value. BUT the overflow: hidden on the containing element chops off any overflow after the column with the most content.

Confusing? I'm not surprised. But write this code and see what happens. Here's my result:

These two-column tricks work equally well for 3-column layouts, but in their simplest form they may not perform well on all browsers. However, the original authors have come up with work-arounds for non-compliant browsers. For more information, check out the authors' original pages: