Menu Search
Jump to the content X X
Smashing Conf New York

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

Equal Height Column Layouts with Borders and Negative Margins in CSS

“What? Another “Equal Height Columns” article? Enough already!” If this is what you think, then think again because this solution is different. It does not rely on any of the usual tricks. It does not use images1, nor extra markup2, nor CSS33, nor pseudo-classes4, nor Javascript5, nor absolute positioning6. All it uses is border and negative margin. Please note that this article will also demonstrate different construct techniques and will brush up on a few concepts.

1. Centering columns without a wrapper div Link

This layout will be built without a wrapper div:

Two column layout7

The basics Link

We use the background of body and the border of one of the columns to create background colors that vertically fill the “row”.

The markup Link

<div id="header">
	<h2><a href="#">Header</a></h2>
	<p>Lorem ipsum...</p>
</div>

<div id="sidebar">
	<h2><a href="#">Sidebar</a></h2>
	<p>Lorem ipsum...</p>
</div>

<div id="main">
	<h2><a href="#">Main</a></h2>
	<p>Lorem ipsum...</p>
</div>

<div id="footer">
	<h2><a href="#">Footer</a></h2>
	<p>Lorem ipsum...</p>
</div>

Tip: always include links within your dummy text to spot stacking context issues.

About using body as a wrapper:

  • always style html with a background to prevent the background of body from extending beyond its boundaries and be painted across the viewport.
  • never style html with height: 100% or the background of body will be painted no taller than the viewport.

The CSS Link

html {
  background: #9c9965;
}

body {
  width: 80%;
  margin: 20px auto;
  background: #ffe3a6;
}

#header {
  background: #9c9965;
}

#sidebar {
  float: left;
  width: 200px;
  background: #d4c37b;
}

#main {
  border-left: 200px solid #d4c37b;
}

#footer {
  clear: left;
  background: #9c9965;
}

What do these rules do exactly?

  • We style html with a background to prevent the browser from painting the background color of body outside our layout.
  • We style body with auto margin to center the layout; the width is set using percentage. The background declaration is for #main.
  • We style the background of #header to mask the background color of body (the same goes for #footer).
  • The background color of #sidebar matches the border color of #main. This is the trick we use to make our columns appear as being of equal height.
  • The footer clears any previous float so it stays below the columns, at the bottom of the layout.

If you take a look at this first step8, you’ll notice that the heading in #sidebar is not vertically aligned with the heading in #main and that we have some gap at the top and bottom of the #sidebar. This is because out of these two containers, only one is a block formatting context9. So margins do not collapse in #sidebar while they do in #main. This also means that #main will not contain floats and that applying clear:left to any nested element in there will clear #sidebar as well.

So to prevent any float and margin collapsing issues we make all the main boxes block formatting contexts.

#header,
#footer {
  overflow: hidden;
  zoom: 1;
}

#main {
  float: left;
}

#sidebar {
  margin-right: -200px;
}

Note: if things look a bit different in IE 6 and 7 it is because in these browsers default margins do collapse inside block-formatting contexts. The following should also be considered:

  • overflow and zoom on #header and #footer make these elements new block formatting contexts.
  • For #main we use float rather than overflow to prevent potential issues if we had to offset some nested content.
  • The negative margin keeps #main into place because now that it is a float, its border box comes next to the margin box of #sidebar (the negative vlaue must match the dimension of the said box).

If you look at the second step10, you’ll see that the border of #main hides #sidebar. This is because of the stacking context. In the flow (tree order), #main comes after #sidebar so the former overlaps the latter.

Positioning #sidebar brings it up in the stack.

#sidebar {
  position: relative;
}

Note: if you make #main a new containing block you’ll revert to the original stack order. In this case, you’ll need to use z-index to keep #sidebar on top of #main.

If you look at step three11, you’ll see that we are almost there. The last things to do are mostly cosmetic. I’ve inserted a base styles sheet1712:

<link rel="stylesheet" type="text/css" href="http://tjkdesign.com/ez-css/css/base.css">

and then added these rules:

html {
  height: auto;
}

body {
  border: 1px solid #efefef;
}

#header,
#main,
#sidebar,
#footer {
  padding-bottom: 2em;
}

Why do we need these rules?

  • We can reset the height on html so the background of #main is not cut-off at the fold (this styling is inherited from the base styles sheet).
  • We can draw a border all around the layout.
  • Because the base styles sheet only sets top margins, we can create gaps at the bottom of the main boxes via padding.

Note: The rule for html is shown here, but it makes more sense to remove that rule from the base styles sheet rather than overwriting the declaration here.

This is the last step13 for this first layout. It relies on these simple rules:

html {
  height: auto;
  background: #45473f;
}

body {
  width: 80%;
  margin: 20px auto;
  background: #ffe3a6;
  border: 1px solid #efefef;
}

#sidebar {
  float: left;
  position: relative;
  width: 200px;
  margin-right: -200px;
  background: #d4c37b; /* color must match #main's left border */
}

#main {
  float: left;
  border-left: 200px solid #d4c37b; /* color must match #sidebar's background */
}

#header,
#footer {
  clear: left;
  overflow: hidden;
  zoom: 1;
  background: #9c9965;
}

#header,
#main,
#sidebar,
#footer {
  padding-bottom:2em;
}

2. Creating a 2-col-layout with two borders in between the columns Link

We’ll build this one with a single wrapper for our two columns, and we want to paint a vertical border between the two columns.

Two column layout with two vertical borders14

The basics Link

The wrapper div allows us to be a bit more creative here. The background of the wrapper is used to paint the background of one column, while its left border is used to paint the background color of the other column. The vertical border will be done by overlapping the right border of the left column with the left border of the right column.

Note: if you have use a fixed width layout (vs. fluid like here), then the wrapper can be used to create the two background colors as well as the vertical border at the same time. This is done by using the left border for the left column, the right border for the right column and the background for the vertical border. Yes, this means the content box is one pixel wide and that negative margins are used to pull the columns into place.

Markup Link

<div id="header">
	<h2><a href="#">Header</a></h2>
	<p>Lorem ipsum...</p>
</div>

<div id="wrapper">
  <div id="sidebar">
  	<h2><a href="#">Sidebar</a></h2>
  	<p>Lorem ipsum...</p>
  </div>
  <div id="main">
  	<h2><a href="#">Main</a></h2>
  	<p>Lorem ipsum...</p>
  </div>
</div>

<div id="footer">
	<h2><a href="#">Footer</a></h2>
	<p>Lorem ipsum...</p>
</div>

CSS Link

We start with the generic rules from the previous demo:

html {
  background: #45473f;
}

body {
  width: 80%;
  margin: 20px auto;
  background: #ffe3a6;
}

#header,
#footer {
  overflow: hidden;
  zoom: 1;
  background: #9c9965;
}

#sidebar {
  float: left;
  width: 200px;
}

#main {
  float: left;
}

To which we add position: relative:

#wrapper {
  display: inline-block;
  border-left: 200px solid #d4c37b;
  position: relative;
}

#sidebar {
  margin-left: -200px;
  position: relative;
}

Note: there is no need to use clear on the footer since #wrapper contains both floats.

  • Rather than using overflow/zoom, we use inline-block to create a new block formatting context (this declaration also triggers hasLayout). The left border will paint a background color behind #sidebar.
  • Negative margin is used to bring #sidebar outside the content box of the parent’s container (to make it overlap the border of #wrapper).

The case of IE6: If the above rules use position: relative (twice), it is because of IE 6. It is applied on #wrapper to prevent #sidebar from being clipped outside of its content box. It is also applied on #sidebar to make sure that the elements are “always” painted with the proper offset.

If you look at this first step15, you’ll see that we have everything working, but the vertical border is in between the columns. You should also notice that in browsers other than IE 6 and 7, there is a small gap at the bottom of #sidebar (at the bottom #wrapper actually). This is because #wrapper is styled with inline-block so it is sitting on the baseline of the line box. The gap you see is the “descender space” (the space reserved for descenders in lowercase letters).

So these are the rules to remove the gap and create the vertical border:

#wrapper {
  vertical-align: bottom;
}

#sidebar {
  margin-right: -1px;
  border-right: 1px solid #888;
}

#main {
  border-left:1px solid #888;
}

What do these rules do?

  • vertical-align: bottom makes #wrapper sit at the bottom of the line box rather than the baseline.
  • the two borders (for #sidebar and #main) overlap because of the negative right margin set on #sidebar. This overlap guarantees that this “common” border will be as tall as the tallest column.

If you look at step two16, things look much better. The last things to do is to add the base styles sheet1712 and the same rules we used at the end of the first demo:

<link rel="stylesheet" type="text/css" href="http://tjkdesign.com/ez-css/css/base.css">

and then add these rules:

html {
  height: auto;
}

body {
  border: 1px solid #efefef;
}

#header,
#main,
#sidebar,
#footer {
  padding-bottom: 2em;
}

This last demo18 for this layout includes the above rules.

3. Creating a three column layout with a border in between the columns Link

We’ll build a layout with a single #main-wrapper, one containing all the divs. This approach complicates things a bit, but it also allows to tackle new challenges. Please note that with this layout, the vertical borders will not show in IE 6 and 7.

Three column layout with vertical borders19

The basics Link

We use the wrapper to create the background of the three columns. The left and right borders of the wrapper will be used for the two side bars while its background will be used for the main content.

The markup Link

<div id="wrapper">
  <div id="header"><h2><a href="#">Header</a></h2><p>Lorem ipsum...</p></div>
  <div id="sidebar"><h2><a href="#">Sidebar</a></h2><p>Lorem ipsum...</p></div>
  <div id="aside"><h2><a href="#">Aside</a></h2><p>Lorem ipsum...</p></div>
  <div id="main"><h2><a href="#">Main</a></h2><p>Lorem ipsum...</p></div>
  <div id="footer"><h2><a href="#">Footer </a></h2><p>Lorem ipsum...</p></div>
</div>

CSS Link

We start with the generic rules from the previous demos:

html {
  background: #45473f;
}

body {
  width: 80%;
  margin: 20px auto;
  background: #ffe3a6;
}

#header,
#footer {
  overflow: hidden;
  zoom: 1;
  background: #9c9965;
}

#sidebar {
  float: left;
  width: 200px;
}

#main {
  float: left;
}

To which we add:

#wrapper {
  border-left: 200px solid #D4C37B;
  background-color: #ffe3a6;
  border-right: 200px solid #D4C37B;
}

This code sets the background color for the three columns. In the same sequence as the above declarations.

If you look at this first step20, you’ll see that we have achieved the background effect we are looking for, but things look pretty broken. Everything shows inside the wrapper’s content box.

These next rules should fix the display of the three columns (zoom: 1 for the #wrapper and position: relative for #sidebar and #aside):

#wrapper {
  zoom: 1;
}

#sidebar {
  margin-left:-200px;
  position: relative;
}

#aside {
  float: right;
  width: 200px;
  margin-right: -200px;
  position: relative;
}

#aside is given a width and floated to the right. The negative margins pull each side bar over the wrapper’s border — outside of the content box.

Note:IE 6 and 7 needs #wrapper to have a layout, hence the use of zoom. IE 6 needs the two position declarations for the same reason as in the previous demos.

If you look at step two21, you’ll see that #header does not stretch across the entire layout and that #footer is nowhere to be found.

These two rules should take care of everything:

#header,
#footer {
	margin-left: -200px;
	margin-right: -200px;
	position: relative;
}

#footer {
  clear: both;
}

The negative margin on both sides of #header and #footer stretches the two boxes outside of the wrapper’s content box. clear:both makes the footer clears all the columns. This is step three22.

Once again, the position declaration is for IE 6. Just remember to always position elements that you offset.

What’s next? Link

You know the drill. We insert a base styles sheet in the document:

<link rel="stylesheet" type="text/css" href="http://tjkdesign.com/ez-css/css/base.css">

and add the usual:

html {
  height: auto;
}

body {
  border: 1px solid #efefef;
}

#header,
#main,
#sidebar,
#footer {
  padding-bottom: 2em;
}

Step four23 shows how things look before we tackle the vertical borders.

Adding vertical borders Link

The following technique is inspired from the companion columns24 technique (Ingo Chao) and the Nicolas Gallagher method25.

To get the effect we want (two borders touching each other), we use generated content to which we apply a background color and a border.

The CSS Link

html:before {
	content: ".";
	position: absolute;
	height: 20px;
	background: #45473f;
	left: 0;
	right: 0;
	z-index: 2;
}

body {
  border-top: 0;
}

#header {
  border-top: 1px solid #fff;
}

#wrapper {
  position: relative;
}

#header,
#footer {
  z-index: 1;
}

#wrapper:before,
#wrapper:after {
  content: ".";
  position: absolute;
  width: 1px;
  height: 2000px;
  background: #9c9965;
  bottom: 0;
}

#wrapper:before {
  left: 0;
  border-left: 1px solid #fff;
}

#wrapper:after {
  right: 0;
  border-right: 1px solid #fff;
}

body {
	position: relative9;
	z-index: -1;
}

OK, so what’s going on here?

  • The fake borders get out of the container (at the top), so the first rule paints generated content on top of them. Note that we would not need this rule if the color of the fake borders was the same as the page’s background (html), or if there was no gap between the top of the viewport and the layout.
  • Because these borders are painted over the border around body, we move the top border from body to #header.
  • To properly position the fake borders, we need to make the wrapper the containing block for the generated content.
  • We bring #header and #footer above the stack so they hide the fake borders which are painted inside the wrapper (from bottom to top).
  • This is the generated content we use to create the columns.

The case of IE 8: The last rule is for IE 8. Without this, IE 8 would not paint the generated content over the borders that escape the wrapper (at the top). If this declaration is sandboxed via the “9” hack, it is because Gecko browsers would make everything unclickable/unselectable.

Note: these pseudo-classes are not supported by IE 6 and 7, so in these browsers, there are no borders between the columns.

Things to consider Link

The third layout uses one main wrapper, but it would make more sense to use a inner wrapper instead to hold only the columns. In case this route was taken here, then it was only for those of you who are stuck with this type of construct, but want to implement this solution for equal height columns.

When absolutely positioning elements inside a containing block with wide columns like in the last two demos, remember that the reference is the padding box, so 0 for right or left may not be the value you would want to use.

Further reading Link

(ik) (vf)

Footnotes Link

  1. 1 http://www.alistapart.com/articles/fauxcolumns/
  2. 2 http://matthewjamestaylor.com/blog/ultimate-multi-column-liquid-layouts-em-and-pixel-widths
  3. 3 http://css-tricks.com/fluid-width-equal-height-columns/
  4. 4 http://css-tricks.com/fluid-width-equal-height-columns/
  5. 5 http://www.paulbellows.com/getsmart/balance_columns/
  6. 6 http://24ways.org/2008/absolute-columns
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-1/step-4.html
  8. 8 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-1/step-1.html
  9. 9 http://www.tjkdesign.com/articles/block-formatting-contexts_and_hasLayout.asp
  10. 10 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-1/step-2.html
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-1/step-3.html
  12. 12 http://thinkvitamin.com/design/setting-rather-than-resetting-default-styling/
  13. 13 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-1/step-4.html
  14. 14 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-2/step-3.html
  15. 15 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-2/step-1.html
  16. 16 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-2/step-2.html
  17. 17 http://thinkvitamin.com/design/setting-rather-than-resetting-default-styling/
  18. 18 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-2/step-3.html
  19. 19 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-3/step-5.html
  20. 20 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-3/step-1.html
  21. 21 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-3/step-2.html
  22. 22 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-3/step-3.html
  23. 23 https://www.smashingmagazine.com/wp-content/uploads/demos/demo-3/step-4.html
  24. 24 http://www.satzansatz.de/cssd/companions.html
  25. 25 http://css-tricks.com/fluid-width-equal-height-columns/
  26. 26 http://www.alistapart.com/articles/fauxcolumns/
  27. 27 http://matthewjamestaylor.com/blog/ultimate-multi-column-liquid-layouts-em-and-pixel-widths
  28. 28 http://css-tricks.com/fluid-width-equal-height-columns/
  29. 29 http://css-tricks.com/fluid-width-equal-height-columns/
  30. 30 http://www.paulbellows.com/getsmart/balance_columns/
  31. 31 http://24ways.org/2008/absolute-columns
  32. 32 http://www.satzansatz.de/cssd/companions.html
SmashingConf New York

Hold on, Tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Barcelona, on October 25–26, with smart design patterns and front-end techniques.

↑ Back to top Tweet itShare on Facebook

Thierry is passionate about Web Design and CSS. He loves the challenge of solving problems and dreaming up original ideas that get him out of bed in the morning and keep him working late at night. He is a front-end engineer at Yahoo! and owns TJK Design, css-101.org and ez-css.org. His twitter handle is @thierrykoblentz.

  1. 1

    Jeremiah Sherrill

    November 8, 2010 7:40 am

    What no 6 column layout? I am disappointed in all of you. ;-)

    5
  2. 2

    To get equal height columns, it’s easy if you use display: table and display: table-cell properties and values. There’s a lot less CSS involved. Works in IE8+ and other modern browsers. Not so good for IE6 and 7 but I think it’s worth considering.

    -27
  3. 3

    What, no HTML5? For shame.

    -37
  4. 4

    John Flickinger

    November 8, 2010 7:58 am

    I’m not sure I understand the practicality of this. What advantages does it have over something like 3 columns made up of floated block elements with a repeating 1px high background image? More flexibility if the column widths change?

    I don’t like putting things like negative margins or 200px borders in my code because it feels like a hack. There are also lightweight jquery solutions like the matchHeights plugin that finds the column that is the tallest, and adjusts the others to match the height using normal css.

    3
  5. 5

    Nice article, and well written. I particularly enjoyed the idea of leaving out the wrapper div, a step I always feel is extraneous. It’s always good to see CSS used in intuitive ways to simplify our efforts on the web, it’s nice to think there’s more and more potential still left in the language.

    -3
  6. 6

    Or you can use a (html) table of two or three columns. And… it’s done. :)

    -37
  7. 7

    Perhaps this link can contribute this article.

    http://www.code-sucks.com/css%20layouts

    -6
  8. 8

    interesting idea to use the body as the wrapper. i’ve never thought of that before actually. is that a standard practice does anybody know?

    1
  9. 9

    Why not use a simple table?

    -25
  10. 10

    Thanks for mentioning in the beginning of this article
    “It does not rely on any of the usual tricks. It does not use images, nor extra markup, nor CSS3, nor pseudo-classes, nor Javascript, nor absolute positioning. ”

    So i like this post no matter how many other solutions are available.

    3
  11. 11

    Yep. I also would recommend the display:table -options.

    -29
  12. 12

    thats what I was going to say, why not just use a table?

    -37
  13. 13

    Because of the coders revenge: If you are no a good graphic designer, make it hard for the no-coders.
    Soooooooooo sad.

    -15
  14. 14

    Because tables are for tabular data. Not layout.

    23
  15. 15

    but I don’t get it, you are using CSS to imitate a table. Why not just use a table?

    -34
  16. 16

    It doen’s work if you want a more complicated background on your website that uses both the en tag. But for simple lay-outs, it’s a nice trick! Well found!

    -2
  17. 17

    Tim said it best: “Because tables are for tabular data. Not layout.”

    Long ago we used tables because we couldn’t do any better. We had a limited set of tools to work with. We didn’t have the CSS tools for _display_ that we have today. CSS is for display, tables are for data.

    Also, the last time I used a table for layout, a masked man ran by and kicked me in the nuts. I am still afraid of layout tables to this day. Don’t let this happen to you!

    18
  18. 18

    It is about semantics. You use tables for tabular data, giving it semantic purpose. There’s nothing semantic about a table layout.

    6
  19. 19

    This is a CSS article not an HTML article :)

    Very nice and informative article Thierry.

    17
  20. 20

    Table-tag is for showing data in a table or lists, e.g. pricelists

    -4

↑ Back to top