Tuesday, May 22, 2012

3-column backgrounds with CSS

In a recent redesign of my other web site, Jedi.com, I wanted to use a three-column layout with one fixed-width column along each side (containing navigation links and ads) and a variable-width column filling the center (containing the actual page content).  That's simple enough... until you try to use a different background image for the two side columns than is used for the center column.  CSS makes it harder than you'd expect to create multiple columns of equal (but variable) height on a single web page.  After a little searching, I finally found a great method for doing this.

In case you've been living in a cave for the last 10 years (like I nearly was), you know that the proper way to to design a web site is to separate the content from the presentation.  You should put the content into your HTML files and structure it with logical markups like <div>.  You then put all the presentation (colors, formatting, etc) in a separate CSS file that is shared by every HTML file.  This allows you to change the look of your entire web site by editing just one file.

I used to be pretty good with raw HTML back in the 1990's, but I haven't done much with web design in the last dozen years, so my CSS skills were... shall we say, "rudimentary."  I've pretty much had to consult Google for everything I need to do.  Fortunately, there are a multitude of good blogs out there written by talented web designers who love to help out newbies.

The problem with my 3-column layout is that web browsers prefer to make each div only as tall as necessary.  If you put three columns together and give them each a different background (be it colors or images), those backgrounds will extend only as far south as the content in that column.  The remainder of the background (down to the height of the tallest column) will match that of the page body.

In order to make all three column backgrounds the same height, you need to wrap your actual columns in several levels of containers and set the backgrounds in those containers rather in the actual columns that house the content.

I first looked at Steven Bradley's method from the Van SEO Design blog, but it didn't work as well (or maybe I just couldn't adapt it properly) for using a variable-width center column.

Several good methods that I considered using are presented by Chris Coyier on the CSS-Tricks blog.  It was from there that I found my way to Matthew James Taylor and his "Holy Grail" layout for specifying the side columns in pixel widths with a variable-width center content area.  Below is a quick summary of how it works.

If you want three columns (each designated as its own <div>), you need to wrap them in four container divs.  MJT's web site has a diagram showing the layout that I won't duplicate here.  Here's what my HTML ended up looking like.  I've renamed the container classes to what I think makes more sense than MJT's names, but I left comments so you can still follow his instructions.

  <div class="title">
    Title bar (spanning all three columns) goes here
  </div>    /* title */
  <div class="rightbg">      /* previously "colmask" */
    <div class="centerbg">     /* previously "colmid" */
      <div class="leftbg">       /* previously "colleft" */
        <div class="centerwrap">   /* previously "col1wrap" */
          <div class="content">      /* previously "col1" */
            Center column content goes here
          </div>    /* content */
        </div>    /* centerwrap */
        <div class="leftcol">      /* previously "col2" */
          Left sidebar content goes here
        </div>    /* leftcol */
        <div class="rightcol">     /* previously "col3" */
          Right sidebar content goes here
        </div>    /* rightcol */
      </div>    /* leftbg */
    </div>    /* centerbg */
  </div>    /* rightbg */
  <div class="footer">
    Footer bar (spanning all three columns) goes here
  </div>    /* footer */

You'll notice that this layout actually places the center (content) column text higher up in the file than the text of either of the sidebars.  This is good for SEO optimization, because search engines value text at the top of a file more than text at the bottom of a file.  If you've got a bunch of ads before any of your content, it's a strike against you in your page ranking.  This also makes your main content the first thing to be displayed on the page, which is nice if you've got ads or widgets in the sidebars which load more slowly than you'd like.

The HTML is pretty straightforward.  The CSS is where all the magic happens to get the background images lined up with matching heights.  In this example, each sidebar column is exactly 200 pixels wide.

body {
  margin: 0;              /* Remove padding around entire page */
  padding: 0;
  border: 0;
  width: 100%;

.rightbg {
  position: relative;     /* This fixes the IE7 overflow hidden bug and stops the layout jumping out of place */
  clear: both;
  float: left;
  width: 100%;            /* width of whole page */
  overflow: hidden;       /* This chops off any overhanging divs */
  /* Right column background */
  background-image: url("right_column_bg.jpg");
  background-position: right;    /* Both columns' bg images must be right justified */
  background-repeat: repeat-y;

.centerbg {
  float: left;
  width: 200%;
  margin-left: -200px;    /* Width of right column */
  position: relative;
  right: 100%;
  /* Center column background color */
  background-color: #fdfdfb;

.leftbg {
  float: left;
  width: 100%;
  margin-left: -50%;
  position: relative;
  left: 400px;            /* Left col width + right col width */
  /* Left column background */
  background-image: url("left_column_bg.jpg");
  background-position: right;    /* Both columns' bg images must be right justified */
  background-repeat: repeat-y;

.centerwrap {
  float: left;
  width: 50%;
  position: relative;
  right: 200px;           /* Width of left column */
  padding-top: 0em;       /* Center column top padding */
  padding-bottom: 0em;    /* Center column bottom padding */

div.leftcol {
  float: left;
  float: right;           /* This overrides the float:left above */
  width: 160px;           /* Width of left col content (left col width minus left and right pad) */
  position: relative;
  right: 20px;            /* Width of the left-hand side pad on the left col */
  padding-top: 20px;      /* Left column top padding */
  padding-bottom: 20px;   /* Left column bottom padding */
  /* Background color & image are specified in "leftbg" class above */

div.rightcol {
  float: left;
  float: right;           /* This overrides the float:left above */
  width: 160px;           /* Width of right col content (right col width minus left and right pad) */
  margin-right: 60px;     /* Width of right col right-hand pad + left col left and right pad */
  position: relative;
  left: 50%;
  padding-top: 20px;      /* Right column top padding */
  padding-bottom: 20px;   /* Right column bottom padding */
  /* Background color & image are specified in "rightbg" class above */

.content {
  margin: 0 220px;        /* Center column side padding:
                             Left pad = left col width + center col left pad width
                             Right pad = right col width + center col right pad width */
  position: relative;
  left: 200%;
  overflow: hidden;
  padding: 20px;
  /* Background color & image are specified in the "centerbg" class above */
  color: #404040;

So, basically, here's how this works.  Because the three outermost containers actually contain all three content columns, all three containers will naturally be the same height as the longest column.  By putting the column background info in these container divs rather than into the actual content divs, we've ensured that the background will be the full height of the longest column.  The tricky part is shuffling the three container divs sideways to make them line up with the column content divs.

The outermost container (rightbg) is the full width of the rendered page body.  By right justifying its background image, we've placed that under the right sidebar column's content.

The next container (centerbg) is overlaid atop rightbg and obscures it from view.  However, by shifting centerbg to the left by the same width as our right sidebar (200 pixels), we allow just that much of rightbg to remain visible.

Similarly, the next container (leftbg) is again overlaid atop centerbg and obscures it from view.  To make only the proper amount of it visible, it is shifted completely off the page to the left of centerbg, and then back to the right far enough that only 200 pixels is visible in the rendered page.

You can check out the resulting layout at www.Jedi.com (screen shot below), which is another domain I've owned for many years that's devoted primarily to building and driving Jeeps.  I'm also in the process of using this layout to redesign another site of mine that hasn't gone public yet.  Obviously, I'm pretty pleased with how well this layout works.

What other neat tricks have you folks discovered for laying out multiple columns in CSS?  I'm all ears, so please speak up in the comments section below.

No comments:

Post a Comment

Please leave your comment below. Comments are moderated, so don't be alarmed if your note doesn't appear immediately. Also, please don't use my blog to advertise your own web site unless it's related to the discussion at hand.