Grow your CSS skills. Land your dream job.

Fluid Width Equal Height Columns

Published by Chris Coyier

Equal height columns have been a need of web designers forever. If all the columns share the same background, equal height is irrelevant because you can set that background on a parent element. But if one or more columns need to have their own background, it becomes very important to the visual integrity of the design.

THE PROBLEM: Three columns with different amounts of content only grow as tall as they need to individually.


THE DESIRE: Columns are all equally tall, matching the height of the tallest.

If a design is NON-fluid width, this task becomes considerably easier. The best technique to use is Dan Cederholm's Faux Columns where the columns are wrapped in a container element (which you probably already have anyway) and that container has an image background applied to it which repeats vertically and simulates the look of equal heigh columns, even if the elements themselves haven't actually grown.

When fluid width and multiple columns comes into play, this task becomes more difficult. We can no longer use a static image to simulate the look of multiple columns. All hope is not lost though. Below we will investigate a number of different techniques for accomplishing fluid width equal height columns.

View Demo   Download Files

Doug Neiner Method

Doug came up with this on the fly a few month ago during a little nerd chat we had. The idea is to use CSS3 gradients to create the columns. Gradients?! Indeed. We normally think of gradients as a color morphing into another color over distance. However the way we declare gradients with CSS is by declaring "color-stops" which are specific locations where the color will be exactly as specified at that point. The trick is to use overlapping color stops. That way you can get one color to stop and another to begin with no visible "gradient".

Check out how you can get a five-column background, by declaring one color stop at the 0% and 100% mark, and doubles at the 20%/40%/60%/80% marks.

.five-columns { 
	background-image: -webkit-gradient(
		linear,
		left top,
		right top,
		color-stop(0, #eee),
		color-stop(20%, #eee),
		color-stop(20%, #ccc),
		color-stop(40%, #ccc),
		color-stop(40%, #eee),
		color-stop(60%, #eee),
		color-stop(60%, #ccc),
		color-stop(80%, #ccc),
		color-stop(80%, #eee),
		color-stop(100%, #eee)
	);   	
	background-image: -webkit-linear-gradient(
		left, 
		#eee, 
		#eee 20%,
		#ccc 20%,
		#ccc 40%,
		#eee 40%,
		#eee 60%,
		#ccc 60%,
		#ccc 80%,
		#eee 80%,
		#eee 100%
	);
	background-image: -moz-linear-gradient(
		left, 
		#eee, 
		#eee 20%,
		#ccc 20%,
		#ccc 40%,
		#eee 40%,
		#eee 60%,
		#ccc 60%,
		#ccc 80%,
		#eee 80%,
		#eee 100%
	);
	background-image: -ms-linear-gradient(
		left, 
		#eee, 
		#eee 20%,
		#ccc 20%,
		#ccc 40%,
		#eee 40%,
		#eee 60%,
		#ccc 60%,
		#ccc 80%,
		#eee 80%,
		#eee 100%
	);
	background-image: -o-linear-gradient(
		left, 
		#eee, 
		#eee 20%,
		#ccc 20%,
		#ccc 40%,
		#eee 40%,
		#eee 60%,
		#ccc 60%,
		#ccc 80%,
		#eee 80%,
		#eee 100%
	);
}

Because we are using percentages for these color stops, this simulated five-column background-image will stretch and shrink just as you expect them to in a fluid width design. This CSS would be applied to to individual columns, but to a wrapper of all the columns. You can think of it as a modern day extension of Faux Columns. The markup itself would then just be a series of columns inside that wrapper.

<div class="five-columns group">

	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>

</div>

Notice the "group" class which is just the clearfix class so that the parent wrapper retains its height despite containing only floated children (it would normally collapse).

I think this is a rather clever take on the idea. Do note that that only modern Gecko and WebKit browsers support CSS3 gradients so you're Opera and IE visitors will not see the column backgrounds. However, the column structure should remain intact just fine even down to IE 6.

This method allows for source order independence by using negative and positive left margins where necessary to jockey columns into position. See the demo for an example of the Doug Neiner method including putting the first column source order wise into the middle.

Nicolas Gallagher Method

Nicolas Gallagher published a gem of an article about using CSS2 pseudo elements to achieve a number of effects that are otherwise difficult to pull off or that require additional HTML clutter.

The idea is to set the parent wrapper with relative positioning. This sets the context for absolute positioning within. Then we make each of three columns one-third the width of the parent and position them relatively within, pushing them over with relative positioning as needed. This also allows for source order independence.

Two of the visible background coloration columns are generated by absolutely positioned block-level pseudo elements (:before and :after) which are again one-third of the width, but 100% of the height of the parent. They are able to sit below the visible text content of the column by having a negative z-index value. The third "column" is actually just the background color of the wrapper showing through. Since the height of the wrapper will be the height of the tallest column, this works.

.pseudo-three-col {
	position: relative; 
	background: #eee; 
	z-index: 1; 
	width: 100%; 
}
.pseudo-three-col .col { 
	position: relative; 
	width: 27%; 
	padding: 3%; 
	float: left; 
}
.pseudo-three-col .col:nth-child(1) { left: 33%; }
.pseudo-three-col .col:nth-child(2) { left: -33.3%; }
.pseudo-three-col .col:nth-child(3) { left: 0; }
.pseudo-three-col:before, .pseudo-three-col:after {
   content: " ";
   position: absolute;
   z-index: -1;
   top: 0;
   left: 33.4%;
   width: 33.4%;
   height: 100%;
   background: #ccc;
}
.pseudo-three-col:after {
   left: 66.667%;
   background: #eee;
}

Using Tables

I bet some of you are starting to think like this by this time. Hey, I don't blame you. Sometimes a tried and true method that gets the job done is the way to go. One way to sure-fire accomplish the idea of fluid width equal height columns is a dang ol' row of table cells. Just in case you forgot how that looks, it's like this:

<table id="actual-table">
	<tr>
		<td>
			<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
		</td>
		<td>
			<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
		</td>
		<td>
			<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
		</td>
		<td>
			<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
		</td>
		<td>
			<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p>
		</td>
	</tr>
</table>

Give each table cell a percentage width that totals up to 100% and you'll be set. Even if you then apply padding to the cells, the table will jockey itself correctly.

#actual-table { border-collapse: collapse; }
#actual-table td { 
	width: 20%; 
	padding: 10px; 
	vertical-align: top;
}
#actual-table td:nth-child(even) { 
	background: #ccc;
}
#actual-table td:nth-child(odd) { 
	background: #eee;
}

Now let's say that table markup gives you the heebie-jeebies. You can actually use plain ol' div markup if you want but still force it to behave like a table. In that case we'd do something like this:

<div id="css-table">
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
</div>

And then use CSS tables:

#css-table { 
	display: table; 
}
#css-table .col { 
	display: table-cell; 
	width: 25%; 
	padding: 10px; 
}
#css-table .col:nth-child(even) { 
	background: #ccc;
}
#css-table .col:nth-child(odd) { 
	background: #eee;
}

Besides using comfortable markup, we actually save some markup because we can go right from the table to the table cells. No element needs to simulate a table-row.

So are CSS tables the answer to our dreams? They are kinda cool, but they aren't supported in IE 7 so if you are interested in going this route I'd recommend just using actual table markup instead. There is really no significant advantages to using CSS tables.

Both of these methods most significant disadvantage is source-order dependance. There is really no way to have the first column in the source order appear anywhere else than the first column.

One True Layout Method

One of the most classic layouts of all time is the one true layout. In one of the demos, equal height columns is tackled. It's a rather clever technique that still works great today. The idea, as usual, uses a wrapping element for all the columns. This wrapper is set to have hidden overflow, which not only clears the floated columns, but hides anything sticking outside of it. This is particularly important, because we are going to be forcing the height of the columns to be extremely tall, and then cutting them off with the hidden overflow. The magical voodoo here is that while we force the columns taller with a huge amount of bottom padding, we suck the height of the wrapper back up with an equal amount of negative bottom margin. This gives us just the effect we need.

The markup is nothing we haven't seen before:

<div id="one-true" class="group">
	<div class="col"><h3>I am listed first in source order.</h3><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
</div>

Then the CSS is just floated columns with the margin/padding trick.

#one-true { overflow: hidden; }
#one-true .col {
	width: 27%;
	padding: 30px 3.15% 0; 
	float: left;
	margin-bottom: -99999px;
	padding-bottom: 99999px;
}
#one-true .col:nth-child(1) { margin-left: 33.3%; background: #ccc; }
#one-true .col:nth-child(2) { margin-left: -66.3%; background: #eee; }
#one-true .col:nth-child(3) { left: 0; background: #eee; }
#one-true p { margin-bottom: 30px; } /* Bottom padding on col is busy */

Note that the padding on the bottoms of the columns is generated by the content within pushing down, as we can't count on bottom padding on the column itself, as it's busy with its fancy trick. Source order independence here is just like we've already covered, with jockeying around with left margins.

Flexbox Method

CSS3 brings with it the flexible box model. Paul Irish recently blogged about it on HTML5 Rocks. Browser support for this is limited at the time of this writing to WebKit only. Mozilla is kind of supporting it, but all the demos I ever see are of images or very little text. As soon as lots of wrapping text gets involved Firefox falls apart. Here is an example that works great in WebKit and fails in Firefox.

The markup, again, is perfectly clean:

<div id="flexbox">

	<div class="col"><h3>I am listed first in source order.</h3><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p></div>
	<div class="col"><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.</p></div>

</div>

The CSS requires, unsurprisingly, a variety of vendor prefixes to kick off the flexible box model. From there, we set the columns widths and padding, but do not require floats. Telling the box orientation to be horizontal is what gets us the layout we want. We then align the columns vertically setting both box-pack and box-align to start.

The flexible box model does not have any methods for forcing the internal boxes to be of equal height. Most of the demos you will see look like they are, but that's because a height is being explicitly set. Because we are hoping for dynamic height columns, we'll have to borrow from the One True Layout and using the positive/negative padding/margin idea.

Finally, saving the best for last, we can alter the location of the columns just by setting their box-ordinal-group to the desired location. It's best to explicitly set all columns where you want them to be, I found setting only certain ones was problematic. Still, being able to change location without layout hacks is awesome.

#flexbox {		
  display: -webkit-box;
  -webkit-box-orient: horizontal;
  -webkit-box-pack: start;
  -webkit-box-align: start;
	
  display: -moz-box;
  -moz-box-orient: horizontal;
  -moz-box-pack: start;
  -moz-box-align: start;
	
  display: box;
  box-orient: horizontal;
  box-pack: start;
  box-align: start;
	
  overflow: hidden;
}
#flexbox .col {
  width: 27.333%;
  padding: 30px 3% 0;
	
  margin-bottom: -99999px;
  padding-bottom: 99999px;
}
#flexbox .col p {
  margin-bottom: 30px;
}
#flexbox .col:nth-child(1) {
  -moz-box-ordinal-group: 2;
  -webkit-box-ordinal-group: 2;
  box-ordinal-group: 2;
  background: #ccc;
}
#flexbox .col:nth-child(2) {
  -moz-box-ordinal-group: 1;
  -webkit-box-ordinal-group: 1;
  box-ordinal-group: 1;
  background: #eee;
}
#flexbox .col:nth-child(3) {
  -moz-box-ordinal-group: 3;
  -webkit-box-ordinal-group: 3;
  box-ordinal-group: 3;
  background: #eee;
}
The flexbox stuff shown here is a bit old. There is a new syntax available. See The Complete Guide to Flexbox.

Quick Notes

  • Using percentages for layout isn't perfect in WebKit
  • In this demo I used things like :nth-child to target some columns. You'll likely get better cross browser compatibility if you give your columns specific class names and use those class names instead. I was more interested in investigating the theory here, and some of the fancy modern techniques only work in browsers where :nth-child would work anyway.
  • Bonus tip: you can use a Faux Columns-like technique with fluid width columns if you only have two columns. This is in use on the current (v7) design of CSS-Tricks. The layout is 2-column fluid, and the background color of the sidebar comes from this image. The column is able to grow, because the placement of that background uses percentages to get it to adjust correctly as the container element grows in width.
    background: url(sidebar.png) repeat-y 61.7% 0;
  • Flexible layout model is very different from the CSS3 layout module. Apparently the wind is blowing toward flexible layout as far as actual implementation.

 

View Demo   Download Files

Comments

  1. Permalink to comment#

    This is so comprehensive! Great work and a great reference.

  2. Quick note: your Nicolas Gallagher Method link is 404′d Chris.

    I recall one method that I’ve used before and is pretty similar to what Nicolas’ sounds like where you use absolute positioning but the part I never knew was that you can use Absolute positioning on both sides of an element to “suck” it to the edge of it’s relative container. So say if you did absolute with top: 0; and bottom: 0; it will pull the column all the way to the bottom. I’m not sure on browser compatibility but as far as I know it works in IE, which, if it works there … well you know the rest.

    Btw I see you’re doing the JQuery summit Nov. 16, sounds interesting.

  3. Permalink to comment#

    Chris, awesome stuff as always man! I’ll definitely be bookmarking this page. Thanks

  4. Michał Czernow
    Permalink to comment#

    Ad. “Nicolas Gallagher Method”

    I would rather code the pseudo-elements like this:

    .pseudo-three-col:before, .pseudo-three-col:after {
       content: " ";
       position: absolute;
       z-index: -1;
       top: 0;
       left: 33.4%;
       width: 33.4%;
       -height: 100%;- /* delete this */
       bottom: 0; /* add this instead */
       background: #ccc;
    }

    Why bottom: 0; is better than height: 100%? Because it’s so much more flexible. You have control over distances; e.g. bottom: 1px; (try this with height :)).

    I’ve learned this mechanism thanks to Google.

    You should also look out for IE7, gecko 1.9x (K-Meleon, FF2.x) and FF3.0 (though I’m not sure about the latter). You can’t fully control positioning of pseudo-elements in those browsers. They will brake your layout. At least explicitly declared height should be avoided and border (though you could declare border color with rgba or better – hsla, because I think that FF3.0 would ignore that declaration). It may be also healthier to declare content: ''; instead of content: ' ';, but that probably depends :).

  5. Permalink to comment#

    Thanks Chris for this comprehensive article!

    I am a big fan of cross browsers solutions for this kind of “job” so I prefer “Faux Columns” and/or “table” method.

  6. makito
    Permalink to comment#

    here is a good example of such a layout

    http://chikuyonok.ru/2010/01/liquid-site-markup/

  7. Permalink to comment#

    OK if example works fine on all major browsers.

  8. Andy
    Permalink to comment#

    None of the solutions work in IE7 at least not for me. I can’t be bothered trying out anything that even IE7 can’t handle.

  9. Amazing! Only problem i’m seeing is that it doesn’t work on IE6, i was about to completely lose it if it did work there haha. Go figure, why would it work!

    My god IE6 needs to go away already, getting so bored with it

  10. Great article. I’m using javaScript to do my columns.. obviously this isn’t idea – but I felt this was the cleanest method to use.

    What’s your opinion on using jS for this?

  11. Permalink to comment#

    One True Layout method has always worked for me.

    I know this is picky but I just feel weird using #css-table {display: table;}. If it HAS to display exactly as a table there may be a good reason it could just be a table?

    What will come next?
    h2.paragraph { display: table; }
    span.heading { display: h2 }

    Great roundup though!! Really useful.

  12. bill
    Permalink to comment#

    What’s really sad is that the one solution that seems to work across all browsers, yes even back to IE6, are tables. No hacks, no vendor-specific selectors, nada. Sad.

  13. Permalink to comment#

    Hi, what about a jQuery plugin? I usually use http://www.cssnewbie.com/equal-height-columns-with-jquery/ . It’s simple and fast.

  14. “Just in case you forgot how that looks, it’s like this”

    Bwhahaha!

  15. Elastic CSS Framework has been doing this for a while, also resolving the subpixel rounding in all browsers correctly.

  16. Permalink to comment#

    jQuery needs to be one of the options. As others have mentioned it can easily be used to solve this. The fallback is simply uneven columns.

    Also, use jQuery for cross-browser nth-child.

  17. Permalink to comment#

    I think it takes 3, maybe 4 lines of jquery to make this dynamic and work across all browsers. nth-child ftw!

  18. I read about Paul Irish’s Flexbox method, and I am really digging the simplicity of it. The problem with solutions like that is that it is so damn limited in browser support. I wonder if someone has something like PIE that will handle those kind of properties.

  19. The CSS framework YAML provides a production ready crossbrowser solution for flexible columns with equal heights since almost two years now (January 2009), based on CSS-tables (for modern browsers) and the One true Layout-Method for IE.

    http://www.yaml.de/fileadmin/examples/06_layouts_advanced/equal_height_boxes.html

  20. Permalink to comment#

    Chris,
    Great article, I’ve been designing for about 2 years, and this has always stumped me.

    Also, Very nice site update… I don’t know how long the new look has been up, but it looks great!

    Cheers!

  21. Scott
    Permalink to comment#

    There was quite a nasty bug with the ‘one true layout’ in Firefox. Page anchors can cause the page content to disappear. It was definitely present in version 3, but it could have been fixed by now. Aside from that it’s the best method of the lot IMO.

  22. firestarter
    Permalink to comment#

    You want a bunch of columns in a table to behave as you expect them to. Use the good old element! Sure it might not be sexy and funky and everything else but it .

    JS, CSS and Javascript ‘methods’ ffs?! Why do we need so many complicated and creative solutions to something that’s not a problem in the first place?

    Why do we forget the simplest of solutions? It’s like using a plasma cutter to cut out a piece of paper? Did we forget how to use scissors?

    Some of us don’t have time to reinvent the wheel….

    • I totally get the desire to use a simple, time-proven method. I included two methods for using tables for this in the article. However, one significant reason to look for CSS methods is the reason we use CSS at all, abstraction of layout and design from markup. The class example: let’s say you have 1,000 pages of HTML marked up using a three column layout with tables. Now the design needs to change and left and right columns need to be swapped. If your layout was CSS, you could do it with one change to the CSS. If your layout is tables right in the markup, you’ll be making 1,000 changes.

  23. firestarter
    Permalink to comment#

    Use the good old <TABLE< element! Sure it might not be sexy and funky and everything else but it works everywhere.

  24. Charles Zipp
    Permalink to comment#

    Good stuff, the simulated table using divs actually helped me resolve an issue I had in one of my sites. I was having to resort to a table within a div which i know is a no no but, this takes care of having to use that workaround.

  25. Thank you for sharing the code for the different methods, the link in the comments to Matthew James Taylor was fantastic, Thank you Dennis. LT

  26. Permalink to comment#

    Great article, Chris.

    Wasn’t aware of the various types of methods to accomplish equal height columns!

  27. Chris White
    Permalink to comment#

    Why didn’t you mention Javascript solutions?

    jQuery gives you the height of a box. You can compare the heights of all your elements, get the highest, then set the rest of the elements to that height. Simple.

    I used this technique for work;

    http://www.webanywhere.co.uk/education/products

    Click “View Products”

    You can the View Source of the Javascript file and search for “#packages .package-fold” to see the actual technique.

  28. Lalit
    Permalink to comment#

    This example are not working in any IE (IE6, IE7, IE8) Browser.

  29. Leighton Price
    Permalink to comment#

    I ran into this a year or two ago and ended up using a neat little jQuery plugin by Tom Deater http://www.tomdeater.com/jquery/equalize_columns/ The client needed the site to work in ie6 so this seemed like the most easily implemented and robust solution.

  30. The following technique does not use images, nor extra markup, nor CSS3, nor pseudo-classes, nor Javascript, nor absolute positioning.

    All it uses is border and negative margin:

    http://www.smashingmagazine.com/2010/11/08/equal-height-columns-using-borders-and-negative-margins-with-css

  31. Great roundup… I’ve come up with a new pure CSS cross browser solution that you may be interested in: http://thelucid.com/2010/12/03/the-solution-to-fluid-inconsistencies-and-equal-height-columns/

  32. Forgot to mention, it works in Safari, Chrome, Firefox, and IE6 and up.

  33. Great, works as expected. i especially like the padding and negative margin method. helped me solve a question posted on Stack-Overflow. Kudos!

  34. Sam

    Note that the gradient method is not reliable in Chrome (currently v35) as they optimise using a 256 scale giving rounding errors – so the colour breaks are not in the correct position.

    See this pen ( http://codepen.io/elliz/pen/fCsay ) for cool animated demonstration (if you are using Chrome) and links to SO questions/discussion and chrome bug listing.

  35. anyone knows how to solve this issue:
    in the “One True Layout Method” if you have a particular <p> with an id like lipsum and then load the page as mypage.html#target, the rest of the content is overflowed:hidden so, there isn’t more in page…

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".