Sticky CSS footers: The flexible way

Update: I’ve developed the idea of this article a bit further to a simple CSS based layout grid, which also includes flexible columns and some other improvements. However, this article is still worth reading to get a basic idea of how the flexible footer works.

A CSS sticky footer is an area, that stays at the window bottom if there is not enough content to fill the complete window height, but stays below the content if its height exceeds the window height. There are several ways how to achieve this circulating in the web, but they all have some flaws: Most of them (e.g. CSSStickyFooter) require a fixed height footer and the only flexible solution I’ve found needs jQuery. However, here is an easy and plain CSS solution for sticky footers with fluid height that works perfectly with IE8+, FF2+, Chrome9+, Safari3+ and Opera 8+ (probably more) and returns acceptable results in IE7 and IE6.

For those of you, who don’t care about big explanations, I’ve made a simple example page and a bit more complex one to show you how it will look like. You can check out the code (it’s quite simple) in your browser or download everything as an ZIP archive or read on, if you want to know all the details.

Three steps to happiness

The basic thought behind the idea is simply to use a CSS table instead of block elements – Wait! Isn’t the use of tables for layout purposes from the dark side of web design!? Well, sort of. If we would use the HTML table tag here, I would totally agree, but we’re using the CSS display: property here, which is purely visual with no semantic purpose at all, so I can’t see any problem here. But enough talk, here is how we do it.

First of all we need the HTML that we’re going to style:

<body>
    <header><h1>Catchy header</h1></header>
    <section><h2>Awesome content</h2></section>
    <footer><h3>Sticky footer</h3></footer>
</body>

Pretty straightforward – no mystical div tags or strange nestings – just plain HTML5.

Step 1: By default the body of a HTML page has not the height of the browser window, but expands with the content of the page. So how do we make it match the window size? Setting the height of the page body to 100% won’t do the job. The problem is, that the height of the whole page itself – yeah right, we’re talking about the html tag here – has to be set, too. So the first CSS style looks like this:

html, body {
    height: 100%;
    margin: 0pt;
}

The margin is set to zero just to make sure there are no spaces that mess up the layout.

Step 2: Now comes the CSS table stuff. What we need is a frame which contains the table rows, the row elements itself and a possibility to expand a row to the maximum possible height. Setting the height of an display:table element (the frame) to 100% will cause it to expand over the whole available height, but it will still grow beyond it, if its content needs more space. So it works more like the min-height: attribute, but that’s exactly what we need here. width:100% ensures that the frame always spans the the full available width. The table rows are simply display:table-row elements. height:1px will ensure that the normal rows will use as few space a possible, while the remaining space will be distributed among the expanded rows with height:auto.

.Frame {
    display: table;
    height: 100%;
    width: 100%;
}
.Row {
    display: table-row;
    height: 1px;
}
.Row.Expand {
    height: auto;
}

That’s basically all the CSS we need to get a sticky and flexible footer in modern browsers.

Step 3: All we have to do now is to put our CSS and HTML code together. Fortunately we can use the body element as .Frame, so there is no need for an extra div tag or so.

<!DOCTYPE HTML>
<html>
<head>
    <style type="text/css">
        html, body {
             height: 100%;
             margin: 0pt;
        }
        .Frame {
             display: table;
             height: 100%;
             width: 100%;
        }
        .Row {
             display: table-row;
             height: 1px;
        }
        .Row.Expand {
             height: auto;
        }
    </style>
</head>
<body class="Frame">
    <header class="Row"><h1>Catchy header</h1></header>
    <section class="Row Expand"><h2>Awesome content</h2></section>
    <footer class="Row"><h3>Sticky footer</h3></footer>
</body>
</html>

Note: Remember to include the html5shiv workaround in your page (and define appropriate CSS styles) if you want to use HTML5 tags in IE8 and below. Or simply use div tags instead of header, section and footer.

A word on older browsers

The code above will work even with older versions of Firefox, Opera and Safari, so there is nothing to worry about here, but unfortunately Internet Explorer 7 and below don’t know anything about display:table or display:table-row, so we go the way of graceful degradation here.

The first thing we have to to is, to prevent the margins of elements inside the row from being outside of it, by adding overflow:hidden to the .Row style.

That gives us quite acceptable results, but you will always have scrollbars due to the 100% height of the .Frame. The solution is to set height:100% in a way that all Internet Explorer versions below 8 won’t recognize. I’m using the (valid) html>/**/body CSS hack to accomplish this, but you might also use conditional comments if you feel better that way. However, here is the fixed CSS:

.Frame {
    display: table;
    width: 100%;
}
html>/**/body .Frame {
    height: 100%;
}
.Row {
    display: table-row;
    height: 1px;
    overflow: hidden;
}
html>body .Row.Expand {
    height: auto;
}

I guess it shouldn’t be too complicated to create a sticky footer by adjusting the height of .Row.Expand with some little Javascript.

Some final words

That’s it, you now have a flexible sticky footer made of valid HTML/CSS, which renders correctly in all common browsers and returns acceptable results in older browsers. Static heights will work as well (just the height attribute for a specific row and I think it’s quite simple to add columns to the layout by setting display:table-cell for the child elements of the rows. Finally once again the links to the simple and complex examples and the ZIP archive.

Errata

Opera height issues: Opera uses the full height of the .Frame element to convert height values from percent to pixels. This causes each expanded row, which contains a child element with height:100% or similar, to be as high as the whole frame. Fix: Use height:1px for normal rows and and height:auto for expanded rows. (all examples fixed on 2012-03-06)