(update July 2022: This tutorial is obsolete. Now we tend to use flexbox or grids.)
My ultimate CSS layout
The goal is to have a 3-columns, liquid layout with the cleanest possible html code. By clean, I mean something like this:
<!doctytpe html>
<html>
<head><link rel="stylesheet" href="example.css" /></head>
<body>
<header> header header header header header </header>
<article> article article article article article </article>
<nav> nav nav nav nav nav </nav>
<aside> aside aside aside aside aside </aside>
<footer> footer footer footer footer footer </footer>
</body>
</html>
Header first, then the main content. Side columns come last. This is
better for text browsers, search engines, and Über Geeks who disable
CSS. Note that I use html5 tags avoid clutter. The actual examples
use <div>
tags.
Default layout
First, the colors. They help visualizing the layout. If you are using Firefox, you should also try the firebug extension. It helps visualizing even the margins of the boxes.
/* html5 reset */
article, nav, aside, header, footer { display:block;
padding:1em; }
/* colors */
html { background-color:#fff; }
body { background-color:#ddd; }
header, footer { background-color:#aaa; }
nav, aside { background-color:#8aa; }
article { background-color:#a8a; }
The result is here.
Fixed width layout
The total width is specified in ems. Don't use pixel widths, they
don't scale properly. The trick here is negative margins.
<article>
comes first and is 30ems wide (this count it's "width"
plus the padding and left margin. <nav>
should fit on the right, but
a negative margin of 30ems push it on the left. This leaves enough
room for <aside>
, which fits on the right.
body { width:35em; margin:auto; }
article { float:left; margin-left:5em; width:23em; }
nav { float:left; margin-left:-30em; width: 3em; }
aside { float:left; margin-left:0; width: 3em; }
footer { clear:both; }
The result is here.
Liquid layout with percentages
The technique is exactly the same. Just replace all em mesures by percentages.
article, nav, aside, header, footer { padding:4% }
body { width:50%; margin:auto; }
article { float:left; margin-left:18%; width:56%; }
nav { float:left; margin-left:-82%; width:10%; }
aside { float:left; margin-left:0; width:10%; }
footer { clear:both; }
The result is here (you may see a rounding error resulting in an 1-off pixel). See what happens when you resize your browser.
Ultimate: liquid layout with fixed side columns
Percentages are not ideal. Side columns generally have few text, and
should vary with the font size only. We need <article>
to be as
wide as <body>
, minus the width of the side columns. We can achieve
this by not specifying the width of <article>
. It will then
expand as much as it can, minus the specified margins.
body { width:50%; margin:auto; }
article { float:left; margin-left: 5em; margin-right:5em }
nav { float:left; margin-left:-35em; width: 3em; }
aside { float:left; margin-left: -5em; width: 3em; }
footer { clear:both; }
The result is here. Do we have found the Holy Grail?
Compromise
No. When <article>
doesn't have enough content, it narrows and
wrecks havoc in the layout. The resulting catastrophy is
here.
I found 2 ways to compensate. The first is to artificially add content
to <article>
, with some additionnal CSS:
article:after{ content:"m m m m m m m m m m m m m m m m m m m";}
The result is here. Don't forget to change the font color, so it matches the background:
article:after{ color:#a8a; }
The result is here. The letters will disapear, but the gap will remain.
The second way is to pollute the html code a bit: wrap <article>
in
a <div id=wrap>
tag:
<!doctytpe html>
<html>
<head><link rel="stylesheet" href="example.css" /></head>
<body>
<header> header header header header header </header>
<div id=wrap>
<article> article article article article article </article>
</div>
<nav> nav nav nav nav nav </nav>
<aside> aside aside aside aside aside </aside>
<footer> footer footer footer footer footer </footer>
</body>
</html>
And the CSS:
body { width:50%; margin:auto; }
#wrap { float:left; width:100%; padding:0; margin:0; }
article { margin-left: 5em ; margin-right:5em }
nav { float:left; margin-left:-100%; width: 3em; }
aside { float:left; margin-left:-5em ; width: 3em; }
footer { clear:both; }
The result is here. Note that
it works well with little content. Because <article>
is not a float
any more, it will extend to its maximum width no matter what.
How to choose
If you want absolutely no ugliness, you will have to choose a percentage based layout, or an em based one (pixel based sizes are evil and ugly, period). If you must have a liquid layout, you will have to choose where to put the ugliness: in the stylesheet, or in the html code. And don't forget that even liquid layouts have limits. If you let it be too wide, long lines will be hard to read. If you want to support very narrow screens, you should write an alternate style sheet with only one column.