CSS Hack: Floated Sidebar

The Problem

Given an article in two parts, a body and a sidebar, we want a floated sidebar

[body flows around sidebar in upper right corner]

that in non-CSS browsers appears after the body:

[sidebar follows body]

However, to float the sidebar, it must precede the body in the HTML. Is there a solution to this problem?

The requirements for a solution are:

  1. The top of the sidebar is aligned with the top of the body, on either right or left (let's pick right).
  2. The body flows around the sidebar.
  3. In the HTML, the sidebar follows the body.

We'd prefer to accomplish this strictly with CSS, without changing the HTML, but that's not a requirement.

Some Approaches That Don't Work

Absolutely positioning the sidebar doesn't work. We can satisfy requirement #1 by wrapping the body & sidebar in a <div> and absolutely aligning the sidebar in the div's upper right hand corner. However, absolutely positioning the sidebar removes it from the normal page flow, so the body flows behind it. This violates requirement #2.

Floating doesn't combine with other kinds of positioning, so we can't float the sidebar after the body and then move it up.

The Hack

Requirement #2 contains an unwarranted assumption. The body needs to flow not around the sidebar, but around the sidebar's position. That may seem like splitting hairs, but it isn't -- because what if there were something floated where we want to put the sidebar? The body would flow around that space. If we could put the sidebar in that same location, we'd have a solution.

We have all the pieces we need. To begin create a "placeholder" for the sidebar, whose purpose is to force the body to flow around where the sidebar will be. The placeholder is a <div> that precedes the body, floated right. In the CSS make it the same width as the sidebar, but give it non-zero left and bottom margins to prevent text from flowing up against the sidebar. (We'll worry about the height later.) Our page now looks like this:

[placeholder floated to right]

Now wrap the sidebar in its own <div> and absolutely position it. To give it something to align to vertically, wrap both the body and sidebar in a containing <div> with position:relative. Give the sidebar zero top and right values to put it in the upper-right corner of the containing div.

Determining the placeholder's height can be a problem. If the content of the sidebar has a fixed size (e.g., an image), we can give the placeholder an explicit height value. If we don't know in advance how tall the sidebar will be -- that is, if it has any text -- setting a height leads to problems when text is resized. To get around this, we add the sidebar's content to the placeholder, using the generated content trick to put the content in the style sheet (to satisfy requirement #3). This forces the browser's rendering engine to calculate the same height for the placeholder that it will later calculate for the sidebar. It actually renders the sidebar content twice in the same location, once floated, once absolutely positioned:

[sidebar rendered on placeholder]

Et voilà: a floated sidebar that meets the requirements.

Extensions

  • You can use this hack multiple times to create several floated sidebars within an article.

  • By changing margins, you can create margin notes that appear in non-CSS browsers as end notes (with one major restriction). Here's an example.

  • Using @import url(...) print, you can make printed versions of the page look good without creating an alternate HTML "printer-friendly" page.

Consequences

  • Changes must be made to the HTML, although they are relatively minor: the addition of a few classes, a few <div>s, and the placeholder's contents.

  • The content of the sidebar is restricted to what you can insert with generated content (basically, images and text). The text can be styled, but that's about it. Lists and links are out.

  • A nice side effect is that putting a <br clear="all"> tag after the container <div> forces the next element to appear below either the body or the sidebar, whichever is longer.

  • Since the sidebar text is rendered twice (once as generated content, once as "real" content), text-to-speech programs will read the sidebar twice. Adding a CSS media rule should prevent reading of the generated sidebar content.

  • Generated content isn't supported on all browsers as of this writing (2004-01-14). (In other words, this hack doesn't work on Internet Explorer, because IE's broken CSS2 implementation makes it unable to calculate the placeholder's height.)

Discussion

This page came about from trying (and failing) to create a floated sidebar. I'm pleased to have invented a work-around, but don't particularly like it, nor do I plan to use it. Aside from the fact that it doesn't work on a major browser, it's complex and overly clever.

What bothers me most is that body-and-sidebar is a common page layout, yet the CSS model can't handle it. The straightforward way to achieve it doesn't fail gracefully on non-CSS browsers, forcing us to use a hack that (a) requires HTML changes and (b) restricts the sidebar's content. If there were a way to specify element rendering order in non-CSS browsers we'd be fine, but that isn't part of the CSS model.


Last updated 15 August 2004
http://www.rdrop.com/~half/Creations/Writings/TechNotes/floated.sidebar.html
All contents ©2004 Mark L. Irons

Previous: Evolution of an Antispam Strategy ··· Next: Backup Strategy