A List Apart

Menu
Issue № 155

Flexible Layouts with CSS Positioning

by Published in CSS, HTML, JavaScript, Layout & Grids · 119 Comments

This article was prompted by the growing crop of CSS “tips and tricks” articles that have surfaced in the last two years. Typical of these are the three column design making use of left and right fixed columns hanging on their margins; and the use of @import, instead of JavaScript, to feed appropriate style sheets to differently enabled browsers.

Article Continues Below

These ideas are very cool and their authors should rightly be heaped with praise, but I can’t help feeling we’ve been here before.

Remember when you figured out how to make a table of images display without the gap? Or how about when you worked out the browser’s table rendering algorithm and started using “educator” rows to guarantee correct display? Even more sinful, do you remember discovering those “hidden” (non-standard) attributes like marginwidth?

As web designers, we are naturally drawn to tricks, gimmicks, and workarounds. We need to keep our attention on what we are trying to achieve in long run.

Real world problems

  1. I want my designers to use CSS. I don’t want them to hide behind WYSIWYG applications, I want them to truly understand what they are doing.
  2. I want universal, flexible layouts. Ideally, a line of text should be as long as certain number of characters, not some arbitrary pixel measure. I want my designers to create grids that are both intelligent and beautiful. I want my client’s information to be as useful on a 160x160 PDA screen as it is on a 1024x768 PC monitor.
  3. I need the team to spend less time on CSS hacking and more time on direct design implementation.
  4. I need flexibility in grid design. If the concept suggests a six-column grid, then I get a six-column grid. Ditto if it needs to be an eight column section on top of a four column section.

Setting practical restrictions

In searching for the most practical grid-building technique, I’ve imposed some restrictions on myself. These are to do with defining the practicality of the solution for my chosen environment (the design studio) and the people who will use and develop these techniques (Dreamweaver-loving designers).

  1. Some browsers understand the @import directive but not the box model (iCab in particular is a fantastic browser in all but CSS2) so ideally, @import hacks shouldn’t be used.
  2. If there must be filtering to alternate style sheets (often unavoidable) then they should be as similar to each other in structure as possible. In particular, if properties are applied to IDs then the same ID selector should appear in all the alternate style sheets to make changing/developing the grid easier for designers.
  3. Again in the interest of designer-friendliness, every box in the grid should be explicitly named, to allow the applying of typographical or border effects without having to add a selector.

Possible solutions

I’d like to suggest a couple of design methods that go a little way toward satisfying the requirements listed above. Think of these as a bridge to that point two years from now when all designers are trained to think natively in CSS.

For starters, here is how a designer describes a three part multi column layout:

  
content stuff here
content stuff here
content stuff here
content stuff here
content stuff here
content stuff here

In the designer’s head, he has just created the XHTML structure for a one-column top (news introduction?) and a four-column (main stories?) bottom section with a one-column footer. He wants the middle and bottom sections to happily float up and down the page in response to the user’s font size selection without any sections overlapping.

Simple enough, really; now why must he go any further?

He should be able to add any number of columns without the page falling apart and without having to go through serious brainy positional coding.

Another thing—it makes sense to write your DIV structure in the order that you intend the reader to read it, not some back-to-front thing to accommodate a weird backwards-compatibility glitch. The idea is to make creating structured layout as simple as possible.

Absolute v. relative positioning

As far as I can tell, there are two ways to do this. Both make use of the basic “absolute” and “relative” positioning attributes. Where a lot of people get this wrong is that the CSS Box Model defines a position:absolute block as absolute relative to its containing block, and not relative to the page or window.

In our example, this means that a set of four absolutely positioned DIVs can line up inside a container DIV defined as relative, and still float up and down on the page in response to font-size changes — all the while retaining their position in relation to each other. Not only that, the layout holds together regardless of the number of columns.

In theory, we’re done. As ever in web design there has to be a bug and some hacking in response. I outline two methods to implement this grid below. Neither is perfect. One uses JavaScript to manually force a parent DIV to inherit its child’s properties (more later); the other has the simplest possible CSS definition, but requires a sniff to send IE5/Mac a different style sheet.

Method 1: CSS only

Our first method is pure CSS and assumes that the designer or site editor has planned an element of copyfitting into the layout. In practical terms, this might mean knowing that the “Blog” section in the middle of the page is always going to be longer than the nav on the left — not an unreasonable request, in my book.

Here are the main containers defined:

  #top-section {
  position:relative;
  left:0;
  top:0;
  }
  #mid-section {
  position:relative;
  left:0;
  top:0;
  }
  #bottom-section {
  position:relative;
  left:0;
  top:0;
  }

Nothing unusual at this point, except those of you who are familiar with the absolute/relative property will have worked out that these containers will not expand when filled with absolute DIVs.

For these sections to expand — for “mid-col-1” to start just beneath “top-col-1” and above “bottom-col-1” — the three container DIVs must either contain relative DIVs or their height must be calculated and applied by hand.

In this method, the design says that the largest column in the middle section is “mid-col-2” so we set mid-col-1,3 and 4 to absolute, and mid-col-2 to relative. Top and bottom sections contain one relative DIV each, so no problem there. This is what the style sheet looks like:

  #top-col-1 {
  position:relative;
  padding-left:20%;
  padding-right:10px;
  }
  #mid-col-1 {
  position:absolute;
  top:0;
  left:0;
  width:20%;
  }
  #mid-col-2 {
  position:relative;
  top:0;
  left:20%;
  width:40%;
  }
  #mid-col-3 {
  position:absolute;
  top:0;
  left:60%;
  width:20%;
  }
  #mid-col-4 {
  position:absolute;
  top:0;
  left:80%;
  width:20%;
  }
  #bottom-col-1 {
  position:relative;
  padding-left:20%;
  padding-right:10px;
  }

This works well in most standards compliant browsers, but not in IE5 Macintosh Edition, which is the browser I use every day.

The bug has something to do with the calculation of width percentages in a relative container. If you set mid-col-2 to “absolute,” the problem goes away.

Method 2: Passing the height of an absolute DIV to its relative parent

If you choose to go with all columns set to absolute, now you have the problem of “bottom-section” overwriting “mid-section”.

The workaround I am currently playing with involves a small script. (It is currently on the clunky side, so for now I’m using the CSS-only method above.) The trick is to use the onload event handler in the <body> tag to trigger a little script that reads the height of “mid-col-2” and passes it to “mid-section.” This forces “bottom-section” to start further down the page where it’s supposed to.

The following scripting is bare minimum. (It would be more elegant to have a script that walks the document’s DOM tree, applying itself to every container of a certain type. In this way you might pass the height of the longest column instead of arbitrarily passing only “mid-col-2” and you could perform the transfer to any number of relative container DIVs.) If a clever JavaScripter wants to finish the job, I’d be obliged. In the mean time, here’s the little hack:

  function inherit(objidParent,objidChild,objidGrandchild) {
if (document.layers) {
alert('sorry, no pretty layouts for netscape 4');
}
else if (document.getElementById) {
  
Parent = document.getElementById(objidParent);
Child = document.getElementById(objidChild);
Grandchild = document.getElementById(objidGrandchild);Parent.style.height = Child.offsetHeight + 'px';
Grandchild.style.display = 'block';return true
}
  } 

With this method, “bottom-section” is at first invisible to prevent slower machines from displaying the overlapping DIVs:

  #bottom-section {
  position:relative;
  left:0;
  top:0;
  display:none;
  }

After assigning the height of the child to the parent, the script sets the display attribute to “block” — completing the layout.

You could further refine this by calling the script every time you run “styleswitcher” to keep the footer from losing its position on application of the new style sheet.

Moving on

I wanted to achieve a grid system that would let my designers move more quickly and so produce fresher work. Ultimately, the only purpose of a grid system is to be broken. As we come up with these web design ideas, we tend to copy each other and everything tends to look the same. This is my main problem with Flash: the typographic animation routines are so cool, everybody uses them.

You can see a functioning example of this grid system on my web log donkeyontheedge.com, and yes, I guess it does sort of look like everybody else’s.

About the Author

119 Reader Comments

Load Comments