CSS Frames v2, full-height
Way back in August of 2003 I wrote a short article called CSS Frames, in which I described a technique to emulate the visual appearance of HTML frames with CSS. That is more than three years ago, so I think it’s about time to revisit the technique and improve it a bit, especially as the original CSS Frames demo page has been receiving loads of traffic lately.
I only had to take a quick look at the code to tell that it’s been a few years since I made the demo. Looking at old code can be a bit embarrassing sometimes. The demo contains a list of browsers that I had tested the technique in. “Mozilla Firebird 0.61” is mentioned as one of them. Like I said, it was a while ago. So here is an update.
The reason for coming up with the CSS Frames technique was, of course, to avoid having to use HTML frames. If you don’t know why you would want to avoid using frames, read my article Who framed the web: Frames and usability.
Now, on to the updated CSS Frames technique.
The layout: header, full-height content, footer
The layout I describe here will have a fixed header at the top of the page, a scrolling content area in the middle, and a fixed footer at the bottom of the page. The concept can be adapted to other types of frame-like layouts, but I’m leaving that as an exercise for you.
I have also made sure that the content area will stretch to 100 percent of the viewport height even if there isn’t enough content to cause scrolling. For that I used the technique described by Peter-Paul Koch in 100% height.
The layout consists of three main blocks: #header
, #content
, and #footer
. The frame effect is created in two steps. First, #header
and #footer
are given fixed positions at the top and bottom of the viewport respectively. Second, #content
is given padding-top
and padding-bottom
to match the heights of #header
and #footer
.
All three blocks are contained in a div
element with the id
#wrap
to enable easier control of the layout width and horizontal centering. #content
is contained in #content-wrap
because of the 100 percent height technique I’m using. The block that scrolls needs to have a height of 100 %, but it also needs padding-top
and padding-bottom
. That would add up to more than 100 %, so the padding is set on #content
instead. Both have the same background.
Different widths
Depending on whether you want the layout to stretch across the entire width of the viewport or not, you will need to use different widths for the #header
and #footer
elements. Elements that have position:fixed
are positioned with respect to the viewport, so just setting their widths to 100% (you need to set a width or they will shrinkwrap to fit their content) and adjusting the width of #wrap
will not work. #header
and #footer
would still be as wide as the viewport, though unless left
or right
is specified their left edges would be at the left edge of #wrap
, where they would be if they hadn’t been positioned at all.
I’ve prepared two example documents to show the difference. In Example 1, the layout is fully fluid, so #header
and #footer
are 100 % wide. In Example 2 the layout is 40 ems wide, and the widths of both #header
and #footer
are set to that same value.
The CSS
Here is the CSS that is used for Example 1:
html,
body {
margin:0;
padding:0;
height:100%; /* 100 % height */
}
html>body #wrap {height:100%;} /* 100 % height */
#header {
width:100%;
height:5em;
}
html>body #header {
position:fixed;
z-index:10; /* Prevent certain problems with form controls */
}
html>body #content-wrap {height:100%;} /* 100 % height */
html>body #content {padding:6em 1em;} /* 6em = height of #header and #footer + 1em, 1em = give the content some breathing space */
#footer {
width:100%;
height:5em;
}
html>body #footer {
position:fixed;
bottom:0;
z-index:10; /* Prevent certain problems with form controls */
}
And this is what makes Example 2 work, with the differences emphasised:
html,
body {
margin:0;
padding:0;
height:100%; /* 100 % height */
}
html>body #wrap {height:100%;} /* 100 % height */
#wrap {
width:40em;
margin:0 auto;
}
#header {
width:40em;
height:5em;
}
html>body #header {
position:fixed;
z-index:10; /* Prevent certain problems with form controls */
}
html>body #content-wrap {height:100%;} /* 100 % height */
html>body #content {padding:6em 1em;} /* 6em = height of #header and #footer + 1em, 1em = give the content some breathing space */
#footer {
width:40em;
height:5em;
}
html>body #footer {
position:fixed;
bottom:0;
z-index:10; /* Prevent certain problems with form controls */
}
The IE workarounds
Unfortunately, Internet Explorer for Windows up to and including version 6 does not support position:fixed
. Since it is used by too many people to be ignored, a workaround is needed. Amazingly, IE7 does not need any workarounds. It just works! IE5 and 6, however, are sent a few extra rules using conditional comments:
<!--[if lt IE 7]>
<link rel="stylesheet" href="ie.css" type="text/css">
<![endif]-->
The file ie.css
contains the following CSS:
html,
body {background:url(foo) fixed;}
#header,
#footer {
position:absolute;
z-index:10;
}
#header {top:expression(eval(document.compatMode && document.compatMode=='CSS1Compat') ? documentElement.scrollTop : document.body.scrollTop)}
#wrap,
#content-wrap {height:100%;}
#content {padding:6em 1em;}
#footer {top:expression(eval(document.compatMode && document.compatMode=='CSS1Compat') ? documentElement.scrollTop +(documentElement.clientHeight-this.clientHeight) : document.body.scrollTop +(document.body.clientHeight-this.clientHeight));}
This works in IE 5, 5.5, and 6. (Though I didn’t bother to fix the horizontal centering in IE 5.*. It’s time to let go of IE 5.* anyway.)
Scrolling is slightly choppy, but it works. Since expressions rely on JavaScript being enabled this will not work when JavaScript is off. Fortunately all that happens in those cases is that the whole page scrolls. No content is hidden.
Progressive enhancement or graceful degradation?
In browsers that do not support position:fixed
—with the exception of IE5-6—the whole page will scroll, which is a perfectly acceptable fallback. I’m using a child selector to let only the browsers that support it see the position:fixed
declarations. Whether you let browsers that do not support fixed positioning (with the exception of IE5-6/Win) see any CSS at all is a different matter.
This technique works in all modern browsers I have been able to lay my hands on, with one exception. The technique used to make the content area stretch to 100 percent of the viewport height causes some strange behaviour in iCab and IE/Mac. The problems are cosmetic and not too bad, so I think most will be able to live with that.
Other than that, I’m not aware of anything. That doesn’t mean there aren’t any problems however, so please speak up if you find any bugs or have suggestions for further improvement.
A note on scrolling
There are a couple of scrolling-related issues with this technique:
- When you scroll by pressing the space bar or page up/down keys on your keyboard, this technique has a problem in that the document area will scroll too far, hiding part of the unread content behind the header or footer, depending on which direction you’re scrolling in.
- The vertical scrollbar extends above and below the actual area that scrolls, which can feel odd.
For possible solutions to these problems, please see The all new fixed layout by Stu Nicholls.
- Previous post: Unitless line-height bug in Mozilla and Firefox
- Next post: Transparent custom corners and borders, version 2