Page layout using CSS: a very simple example

Rather often people ask how one could use CSS positioning instead of tables for layout. I had been looking for a simple example, but with no luck, so I decided to try and design one. (You are now viewing the styled version. See the unstyled version for comparison.)

So you want nav links on the left?

Suppose you would like to have a set of site navigation links on the left. A very simple idea, which could be implemented using a two-cell table. But the CSS approach has several benefits, including the fact that it can be made to degrade gracefully to purely linear presentation when CSS support is switched off. And it could be done so that then the nav links are after the content proper, which is often desirable. For example, blind users don't like to hear all the links first every time they move from a page to another within a site.

Note that this means assigning some width on the left to the navigational links, across the page vertically, even if this means wasted space below the actual links. It might be more appropriate to make the content take full width after the link section, but this would be a different issue. That approach will be briefly discussed below, though.

Use two div elements and absolute positioning

The simple idea is to make the document body consist of two div elements, one of which contains the content proper and the other contains the nav links. Then I use CSS to make latter div take some specific width, using font size as unit, and using a suitable value corresponding to the widths of the links. And the div that has the content proper is absolutely positioned so that it will appear to the right of the nav links.

The simple example

This means something like the following:

In CSS:
#nav     { width:5em; padding:0.3em;
           background:#fff; color:#000;}
#content { position: absolute; top: 1em; left: 8em; 
           padding-bottom:0.5em; }
#nav hr  { display: none; }
#nav ul  { margin: 0 0 0 1em; padding: 0; }
#nav li  { margin: 0; padding: 0; }
In HTML:
<div id="content">
Content proper goes here
</div>
<div id="nav">
<hr>
<ul>
<li>a link goes here</li>
<li>another link goes here</li>
...
</ul>
</div>

In this example, I just position the content part absolutely about 8 em units from the left. This leaves sufficient space for the small navigational part, which is positioned by the browser appears there as if there were no content part. The idea of absolute positioning is that you specify the position and other elements get positioned there as if the absolutely positioned element were not present at all.

Information about the constructs used

Please see my collection of essential CSS links for information about the properties and techniques used. For positioning in particular, see CSS Positioning, Part I and CSS Positioning, Part II as well as other informative and readable tutorials by by Stephanos Piperoglou.

In particular, note that padding is used to prevent the text inside the navigation box from hitting the box border. Also note that display:none causes the hr element between the content proper and the navigation links to be ignored in presentation, i.e. a horizontal rule will appear only when the style sheet is not applied. The rules for #nav ul and #nav li make the presentation of a list more compact than in typical default rendering. This lets us use the logical construct and still tune the presentation into a small box.

Discussion

I posted an article presenting the idea above to the comp.infosystems.www.authoring.stylesheets newsgroup. It raised an interesting discussion, from which I have picked up a few comments that I found especially interesting.

Rijk van Geijtenbeek remarked:
But everything besides the navigation bar has to go in the 'content' div. If you want to put a separate footer beneath the content over the full length of the page, you're in trouble with this approach.
- -
It doesn't 'work' properly in browsers that support only CSS1 and no CSS positioning (aka Opera 3.5-3.62) The fall back is readable enough.
- -
In Netscape 4.7, the left margin for the UL element gets added to the default margins.
Darin McGrew wrote:

I like the use of ems. It shows that CSS layout can be based on (and thus, can adapt to) the size of the user's font. It isn't that complex a concept that it should cause problems for those migrating from table-based layout to CSS-based layout.

Sander Tekelenburg mentioned the site http://henkhaverhoek.nl, which uses float to position things. And float indeed is another interesting possibility of creating layout in simple CSS. The reason why I chose absolute positioning is mainly that it looked more natural from the perspective of people who have used tables for layout.

He also wrote that the simple positioning approach helps to keep the HTML simple, and easy to maintain. "You can just quickly mark up your content and be done with it. The only 'ugliness' is the odd DIV that you really only need for the CSS."

Stan Brown wrote about problems with Netscape 4.x. Actually, my original idea didn't work at all on Netscape 4.5 in some tests: the navigation block just didn't appear at all! It's up to you to decide what position to take to Netscape 4.x, which is a nest of bugs as regards to CSS support. But for the purposes of my demonstration, I replaced the use of a border, as in
#nav { border: solid 0.1em #060; }
by the setting of background and text color. There might be ways to circumvent the Netscape 4.x bugs so that you can specify a border, instead of or in addition to background, but I'm satisfied with the observation that the navigational area can be made visually distinctive in some manner.

Suppressing the navigation on print

Site navigation links are hardly useful in printed copies. Besides, since they eat up horizontal space when positioned on the left, part of the content proper often get chopped off. It's frustrating to print a long page just to see that the last few characters of each line have been lost.

In the approach described above, it's easy to add a style sheet rule that suggests that the navigation links be omitted in print:

@media print { #nav { display:none; } }

This, however, is not sufficient, since the content would still be positioned, leaving an unnecessary left margin. But since the idea of positioning the nav links on the left probably makes sense on computer screen only, we could simply put our original style sheet inside a @media screen {...} wrapper. This means that the style sheet as a whole would be the following:

@media screen {
#nav     { width:5em; padding:0.3em;
           background:#fff; color:#000;}
#content { position: absolute; top: 1em; left: 8em;
           padding-bottom:0.5em; }
#nav hr  { display: none; }
#nav ul  { margin: 0 0 0 1em; padding: 0; }
#nav li  { margin: 0; padding: 0; } }
@media print {
#nav     { display: none; } }