Using Floats to Display Columns in Any Order






Using Floats to Display Columns in Any Order

Problem

You want to develop a system to display content in columns in any order.

Solution

Given the following markup:

<div id="container-outer">
 <div id="container">
  <div id="content" class="column">
   <div class="wrap">
    [...]
   </div>
  </div><!-- /END #content -->

  <div id="navigation" class="column">
   <div class="wrap">
    [...]
   </div>
  </div><!-- /END #navigation -->

  <div id="related-info" class="column">
   <div class="wrap">
    [...]
   </div>
  </div><!-- /END #related-info -->
 </div><!-- /END #container -->
</div><!-- /END #container-outer -->

Apply the following CSS rules:

.column {
 float: left;
}

#content {
 margin-left: 20%;
 width: 60%;
}

#navigation {
 margin-left: -80%;
 width: 20%;
}

#related-info {
 width: 19%;
}

/* IEx patches \*/
* html .column {
 display: inline;
}

* html #navigation li {
 height: 1%;
}
/**/

This will yield the basic page layout that you see in Figure, with two narrow, flexible-width sidebars bounding an equally flexible center column.

Basic formatting of page layout


From this rather bland foundation, you can layer additional CSS on top of it. Adding the following code to your CSS will yield a design like Figure:

body {
 font: normal 62.5%/1.7 Verdana, Geneva, Helvetica, Arial, sans-serif;
 margin: 0;
 padding: 0;
}
#container:after {
 clear: both;
 content: ".";
 display: block;
 height: 0;
 visibility: hidden;
}
#container {
 display: inline-block;
}
/* Hide from MacIE5 \*/
#container {
 display: block;
}
/**/
#container-outer {
 background: url("bg-left.gif") repeat-y 20% 0;
}
#container {
 background: url("bg-right.gif") repeat-y 80% 0;
}
.column .wrap {
 padding: 20px;
}
#content .wrap {
 padding: 20px 30px;
}
#content p {
 margin-top: 0;
}
#content p:first-child {
 font: normal 1.4em/1.6 Georgia, Times, "Times New Roman", serif;
}
#content p:first-child:first-line {
 text-transform: uppercase;
}
#navigation ul, #navigation ul li {
 list-style: none;
 margin: 0;
 padding: 0;
}
#navigation ul li {
 margin-bottom: .4em;
}
#navigation li a {
 background: #36C;
 color: #FFF;
 border-left: 7px solid #09F;
 display: block;
 padding: .4em .4em .4em 20px;
 text-decoration: none;
}
#navigation li a:hover {
 border-left: none;
 border-right: 7px solid #09F;
 padding-left: 27px;
}
#related-info {
 color: #555;
 font-style: italic;
}
#copyright {
 border: 1px solid #B2B2B2;
 border-width: 1px 0;
 clear: both;
 padding: 10px 20px;
 text-align: center;
}
#copyright p {
 margin: 0;
} 

Fleshed out design of multicolumn layout


Discussion

The float model has a storied history. The authors of the CSS specification never intended floats to be used for page-level layout control: rather, they were a means to control the flow of content around an object, much as align="left" or align="right" would cause text to wrap around an img element. But despite the specification's original spirit, floats do offer us a powerful and flexible alternative to traditional, table-based layout techniques.

Alex Robinson, a designer, published an influential article on creating the "Any Order Columns" in CSS (http://www.positioniseverything.net/articles/onetruelayout/). Robinson's technique allows developers to create multicolumn layouts easily by using floats to display each column in any order, regardless of the order in which those blocks appear in the markup.

The markup

To work with this technique, first you need to establish columns in your markup, like so:

<div id="container">
 <div id="content" class="column">
  [...]
 </div><!-- /END #content -->
 
 <div id="navigation" class="column">
  [...]
 </div><!-- /END #navigation -->

 <div id="related-info" class="column">
  [...]
 </div><!-- /END #related-info -->
</div><!-- /END #container -->

<div id="copyright">
 <p>Copyright notice goes here.</p>
</div>

Inside each div, place any markup you would like. Figure shows what the unstyled document looks like, with a few paragraphs and an unordered list thrown in for good measure.

Unstyled page layout


From this demonstration so far, a div element is set up for each of your three columns, and each is assigned an id that describes the kind of content that will be placed inside. In this solution, the values for id are content, navigation, and related-info. It would have been just as easy to use center, left, and right, but that wouldn't have been especially forward-thinking: what happens when you change your site's CSS file, and the new design requires the "left" div to appear on the righthand side of the page?

Defining the columns

With this simple markup structure in place, you can apply a generic float rule to all three column divs:

.column {
 float: left;
}

As you see in Figure, the layout does not look drastically different. The copyright text is a bit out of alignment, but the bulk of your page appears as it did before with each column div stacking horizontally. Once dimensions are assigned to these blocks, however, things rapidly change.

The copyright notice has moved


First, start with the content block. To set the block to be 60% of the window width, and the width of the lefthand sidebar to be 20% of the screen, create the following rule:

#content {
 margin-left: 20%;
 width: 60%;
}

Figure shows that the layout is looking a bit odd, but starting to take shape.

Applying styles to the content portion of the layout


By setting a lefthand margin equal to the width of your lefthand sidebar, you've essentially "reserved" some space for it. The next step is to use negative margins to "pull" the navigation div across the content div to the lefthand side of the page:

#navigation {
 margin-left: -80%;
 width: 20%;
}

The margin-left value applied is a sum of the width of the center column (60%) and its lefthand margin (20%). This pulls the navigation column over to its proper place (see Figure).

The navigation moves to the left column


Now, simply by setting a width on the related-info block, the three-column layout is complete, as shown in Figure:

#related-info {
 width: 20%;
} 

Moving the right column content into place


Looks excellent, although the copyright div is still a bit off. But with the clear property, that's easily fixed (see Figure):

#copyright {
 clear: both;
}  

Placing the copyright notice at the bottom of the page


Although the layout may look as though your columns are nearly complete, Figure shows you that Internet Explorer on Windows needs a little extra attention.

Problems with the layout are viewed in Internet Explorer for Windows


Thankfully, this is a documented IE bug known as the "Doubled Float-Margin Bug" (http://positioniseverything.net/explorer/doubled-margin.html): essentially, when a margin is applied to a floated box in the same direction as the float, that margin is doubled in size.

Since a lefthand margin is applied to a left-floated element, IE on Windows takes that 20% margin and doubles it to 40%.

Thankfully, the fix is a simple one. By applying display:inline to the problematic element, Internet Explorer behaves again. To do this, add the following lines to your CSS:

/* IEx patches \*/
* html .column {
 display: inline;
}
/**/

The oddly formatted comments and * html prefix ensure that this code is seen by IE on Windows, and IE on Windows alone. And as Figure shows, IE is behaving properly.

The fix is applied so that the layout works in Internet Explorer for Windows


So you've arrived at last: a flexible, three-column layout template. But where else can you take this?

Creating whitespace

The space between the columns is called a gutter. To customize this layout by increasing the size of the gutters, an approach would be to apply some margins around the columns. There are a number of ways to achieve this effect, but first start by adding an additional div to each of your columns, like so:

<div id="container">
 <div id="content" class="column">
  <div class="wrap">
   [...]
  </div>
 </div><!-- /end #content -->

 <div id="navigation" class="column">
  <div class="wrap">
   [...]
  </div>
 </div><!-- /end #navigation -->

 <div id="related-info" class="column">
  <div class="wrap">
   [...]
  </div>
 </div><!-- /end #related-info -->
</div><!-- /end #container -->

With your "wrap" divs in place, apply padding to them with CSS to create more breathing room (see Figure):

.column .wrap {
 padding: 20px;
}

#content .wrap {
 padding: 20px 30px;
} 

Increasing the size of the gutters


Adjusting the order of columns

As you may have noticed by now, the "Any Order Columns" method is grounded in the intelligent use of margins: positive margins are used to reserve space, while negative margins are used to "pull" columns out of their natural position.

Now simplify the CSS for a moment, and remove all the column margins:

#content {
 width: 60%;
}
#navigation {
 width: 20%;
}
#related-info {
 width: 19%;
}

As a result, your layout now looks like Figure, with each column appearing in its natural position in the float order.

Moving the navigation between the columns


By adding a lefthand margin to your navigation div, and then by using a negative lefthand margin to move your related-info div, you can essentially reverse the order of the second two columns. With the following CSS, you're left with a layout like Figure:

#content {
 width: 60%;
}
#navigation {
 margin-left: 20%;
 width: 20%;
}
#related-info {
 margin-left: -39%;
 width: 19%;
} 

Reversing the order to the columns


And to complete the demonstration, place the content column on the righthand side of the page, as shown in Figure.

To do so, apply the following code:

#content {
 margin-left: 40%;
 width: 60%;
}
#navigation {
 margin-left: -100%;
 width: 20%;
}
#related-info {
 margin-left: -80%;
 width: 19%;
} 

Content column moved to the righthand side of the page


As with the first layout, you've applied a margin to the content column in order to "reserve" some whitespace on the lefthand side of our page. Then, you've used negative lefthand margins to pull the navigation and "related information" divs into the proper location.

Page layout algorithm

A simple way to calculate rearranging columns is to follow a somewhat simple algorithm used to calculate the negative margins for a column:

  1. For the column you want to determine its negative margin, first calculate the rightmost point for all columns that precedes it in the source code.

  2. Then specify the leftmost point for the column.

  3. Finally, subtract the rightmost value from the leftmost to give the left margin for the element.

If this technique doesn't work, there's always good ol' trial and error.

Faking columns

Now return to your first layout (see Figure), and see how you can make your columns feel, well, a bit more polished. The first step? Background images.

Initial layout awaiting column graphics


" Faux columns" is a technique developed by web designer Dan Cederholm (http://alistapart.com/articles/fauxcolumns/) that utilizes a horizontally repeating background image.

By using one tiled image, Cederholm's method works incredibly well in a fixed-width design: however, the technique's versatility means that it needs only slight modification to work in our fully flexible layout.

First, you need two images, one for each side of the content column. Figure shows the lefthand graphic, while Figure shows the right.

Graphic for lefthand column


Graphic for righthand column


Next, you'll need to wrap your container block in an extra div, like so:

<div id="container-outer">
 <div id="container">
  [Rest of template goes here]
 </div>
</div>

And finally, you'll need to add the following rules to your style sheet:

#container:after {
 clear: both;
 content: ".";
 display: block;
 height: 0;
 visibility: hidden;
}
#container {
 display: inline-block;
}
/*\*/
#container {
 display: block;
}
/**/
/*\*//*/
#container {
 display: inline-block;
}
/**/
#container-outer {
 background: url("bg-left.gif") repeat-y 20% 0;
}
#container {
 background: url("bg-right.gif") repeat-y 80% 0;
}

With this code in place, the columns appear as full-length columns, like the ones in Figure.

Column graphics are applied to layout


From here, feel free to add any typographic styles you'd like; the ones supplied in the Solution section of this recipe will do nicely, and will yield the finished design shown in Figure.

Finalized page layout


An alternative solution

The float model for laying out pages is powerful, but floats can have a rather steep learning curve. As a result, many designers find absolute positioning to be an attractive alternative, enabling them to precisely position the different components of their design with x- and y-coordinates.

Unfortunately, positioned elements are taken "out of the document flow," which effectively collapses their containing element. As a result, "positioned" designs lack the powerful float concept of clearing, which enables the different parts of your designs to be "context-aware": that is, a footer div (such as the copyright block in the solution, earlier) can be cleared of the floated blocks above it, but not of any positioned elements on the page.

Shaun Inman, a talented web designer/developer, has written a lean JavaScript function to fix this problem (http://shauninman.com/plete/2006/05/clearance-position-inline-absolute.php). When inserted into your web pages, Inman's script will automatically "clear" elements of any other positioned elements on the page (see Figure).

Using absolute positioning for a layout


The only potential drawback to this method is that it does rely on JavaScript being active in the user's browser. But if your content is accessible if you disable JavaScript in your target browsers during testing, then all should be well.

See Also

Recipe 9.9 for designing an asymmetric layout with absolute positioning.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows