October 9th, 2009 - by Paul OB

CSS is a sticky subject in the best of times and to make it more sticky I thought I’d run down the techniques needed to make a sticky footer that works in all modern browsers. This is unlike most examples on the web that break in either Opera, IE8, IE7 or indeed in all three.

Try any of those footers from the Google search above in IE8 or Opera (some don’t work in IE7 either). Load the page then grab the bottom of the window (not the side or corner of the window) and drag it up or down and you will see that the footer usually sticks in the wrong place, messing up the display.

Now try it on my old original sticky footer version (circa 2003 which pre-dates all those above) and you will see that my version is working in all browsers including IE8.

Before we get into details I should first explain what a sticky footer is.

What is a Sticky Footer

A sticky footer is one that sits at the bottom of the viewport when there is not enough content in the page to push the footer down. If there is a lot of content then the footer sits at the bottom of the document and will be below the fold as usual. Why this is desirable is because on short content pages you won’t have a footer right at the top of the screen looking very strange indeed as shown from Figure 1 below.

Figure 1 – normal footer close to content.
Normal footer

Figure 2 – Sticky footer at bottom of viewport.
f2

Note that a “fixed positioned” footer is not the same thing as a sticky footer as a fixed positioned footer is one that sits at the bottom of the viewport at all times and never moves. Don’t get confused between the two.

Overview

Before we get into the nitty gritty detail I will briefly explain the concept in getting a sticky footer to work.

The first thing we need to do is create a 100% high container which is achieved by setting the html and body elements to 100% height and then creating a container that is a minimum of 100% high. The footer is then placed after this container which means it will be invisible as it will be below the fold of the page but by the magic of negative margins we can bring it back into view at the bottom of the viewport.

Of course this means that the sticky footer must be a fixed height (pixels or ems will do) so that we know how to accommodate it with the exact negative margins that bring it into view. This also means that our footer is now overlapping content on the page so we will also need to protect this content with either padding on an inner element, or some other similar approach as you will see when we get into specifics later.

That’s basically all there is to it except that we have to squash a few bugs on the way to make it work everywhere.

When to use a Sticky Footer

Sticky footers are best suited for fixed width sites with small copyright messages and horizontal menu links that keep the footer at a relatively small size. The technique will work with a large height footer but the chances are that if you have a very large footer it will always reach the bottom anyway by the time you’ve got your header and content in place. It will also work with percentage width footers but remember that if the content in the footer is squeezed into making the footer higher than it was then the negative margin routine won’t match any more and the layout will be misaligned slightly.

Creating The Footer

Now it’s time to get your hands dirty with the code and I will break the method down in easy steps.

1) Create a 100% high wrapper to fill the viewport.

This is accomplished by removing the default margin and padding from the html and body elements and then setting a height of 100% so that our wrapper element can base its height on this. We then use min-height:100% on our page wrapper and not height:100% because our container would never grow otherwise but we will still need to address IE6 as it doesn’t understand min-height at all. (If we didn’t apply a height to html and body then our container would collapse to auto height and would not stretch to the bottom. The margins from html and body must also be removed because they would increase the height and thus ruin the effect as we want an exact 100% height only.)

  1. html, body {
  2.     height:100%;
  3.     margin:0;
  4.     padding:0;
  5. }
  6. #outer {
  7.     width:760px;
  8.     background:#ffffcc;
  9.     margin:auto;
  10.     min-height:100%;
  11. }
  12. * html #outer{height:100%}
  1. <body>
  2. <div id="outer">
  3. <!-- all content apart from the footer must go inside this outer wrapper -->
  4. </div>
  5. <div id="footer"></div>
  6. </body>

As mentioned previously IE6 doesn’t understand min-height therefore we provide a rule for IE6 only (using the star selector hack to target IE6 and under only) using the height property instead. We can do this because IE6 treats height as a minimum and will always expand an element to accommodate its content when overflow is set to visible (which is the default). We must also hide this height rule from other browsers because this would limit them to only an initial 100% and therefore the element would never expand with content.

Don’t be tempted to use the !important hack to provide the height property to IE6 because the routine causes IE7 to be buggy with 100% height and won’t resize the footer when the viewport is resized vertically.
i.e. Do not do this:

  1. #outer{
  2.     min-height:100%;
  3.     height:auto!important;
  4.     height:100%;
  5. }

Do not do this either:

  1. #outer{
  2.     min-height:100%;
  3.     height:100%;
  4. }
  5. html>body #outer{height:auto}

Both the above will fail in IE7 due to an obscure bug and should not be used in this routine.

The result of this code is that we now have a container that stretches to 100% height of the viewport quite nicely but the footer is sitting under this element and therefore under the fold of the page.

To bring the footer into view we first have to give it an appropriated height and then using a negative margin we drag it back into view. The negative margin can be applied in either of the following three methods.

1) A negative top margin on the footer itself
2) A negative bottom margin on the main wrapper
3) A negative top margin on the main wrapper

The first two methods are basically the same and just cause the footer to slip up over the bottom of the wrapper by the appropriate amount. Remember that the negative margins must match exactly the height of the footer. As a consequence of pulling the footer upwards it may indeed overwrite any content that was above it so you would also need to add some padding to the content above. The padding couldn’t be added to the main wrapper because that means it would be too high and therefore the padding must be added to an inner element instead or just added to the element above the footer.

The third method mentioned above uses a slightly different tack in that the whole wrapper is moved upwards with a negative margin thus allowing the footer to fall into view at the bottom of the viewport but with the consequence that our wrapper now starts above the top of the viewport and out of sight. We therefore need to soak up this space on an inner element much the same as we did with the other two methods.

In 99% of cases you will most likely have a header element at the top of your page and you can use this element to soak up the extra space. My preferred method is to use a solid border on top of the header to do this because unlike padding it would not interfere with any absolutely positioned children should there be any.

Assuming our footer is 40px high the code to accomplish this is shown below:

  1. #outer {
  2.     width:760px;
  3.     background:#ffffcc;
  4.     margin:auto;
  5.     min-height:100%;
  6.     margin-top:-40px;/*footer height - this drags the outer 40px up through the top of the monitor */
  7. }
  8. #header {
  9.     width:760px;
  10.     background:red;
  11.     border-top:40px solid #fff; /* soak up negative margin and allows header to start at top of page*/
  12. }

The top border soaks up the space that is above the viewport and allows content to start in the viewport where it should be. This is exactly what we wanted and is quite a slick way to do this as we no longer need to worry about any overlap of the footer.

To make it easier to follow here is the code so far:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  5. <title>Sticky Footer at Bottom</title>
  6. <style type="text/css">
  7. * {/* for demo only*/
  8.     margin:0;
  9.     padding:0
  10. }
  11. html, body {
  12.     height:100%;
  13. }
  14. #outer {
  15.     width:760px;
  16.     background:#ffffcc;
  17.     margin:auto;
  18.     min-height:100%;
  19.     margin-top:-40px;/*footer height - this drags the outer 40px up through the top of the monitor */
  20. }
  21. * html #outer {
  22.     height:100%
  23. }
  24. #header {
  25.     width:760px;
  26.     background:red;
  27.     border-top:40px solid #fff; /* soak up negative margin and allows header to start at top of page*/
  28. }
  29. #footer {/* footer now sits at bottom of window*/
  30.     background:red;
  31.     width:760px;
  32.     margin:auto;
  33.     height:40px;/* must match negative margin of #outer */
  34.     clear:both;
  35. }
  36. </style>
  37. </head>
  38. <body>
  39. <div id="outer">
  40.     <div id="header">
  41.         <h1>Header</h1>
  42.     </div>
  43.     <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam mattis tempor imperdiet. Morbi elit dolor, aliquam ut sagittis id, aliquam et tortor. Phasellus vitae suscipit dolor. Fusce euismod leo quis magna varius a feugiat elit aliquam. Suspendisse nec libero tellus. Nam quis libero vel sapien ultrices fermentum id vel sem. Duis massa neque, laoreet at viverra scelerisque, malesuada id nunc. Quisque turpis mi, commodo ac commodo vitae, accumsan eget nibh. Fusce sit amet leo sodales orci porta tempor. Donec nec mollis libero. Aenean porttitor placerat pretium. In hac habitasse platea dictumst. Nam vel dignissim turpis. Aenean facilisis purus nec nisi egestas at scelerisque tellus lobortis. Praesent vitae neque est. Fusce lacinia lectus sed urna suscipit blandit. Vestibulum sed euismod tortor. Sed vel neque nisl. Nunc aliquam feugiat egestas. </p>
  44. </div>
  45. <div id="footer">
  46.     <p>Footer</p>
  47. </div>
  48. </body>
  49. </html>

With the code shown we are very close to have a working footer and indeed the code above will work in IE6/7 and Firefox with no problems. Try out the above and see for yourself.

The problem with the above code is that it doesn’t work properly in IE8 or in Opera. If, as already mentioned, you move the window by grabbing the bottom of the viewport (not the side or the corner) then the footer becomes mis-positioned or doesn’t move at all.

This is the behavior seen in all other sticky footers but it doesn’t occur in my original demo. The clue to why my old demo is still working will provide us with a neat solution that can be wrapped into this specific footer routine.

Back in 2003 not many browsers understood min-height and to accommodate these browsers I used a min-height hack which was basically a 100% high float that was 1px (or even 0px) wide. This held the browser open to the initial 100% height quite nicely without affecting much else as long as we took clear of clearing issues. The 100% height float triggered opera into resizing the page correctly once the viewport was moved and caused the footer to be drawn into the correct position.

Today (and thanks to the quiz I set at sitepoint) we can apply that 100% float using the pseudo element “:before” and negate the need for any extra html mark up at all.

  1. body:before {/* thanks to Maleika (Kohoutec)*/
  2.     content:"";
  3.     height:100%;
  4.     float:left;
  5.     width:0;
  6.     margin-top:-32767px;/* thank you Erik J - negate effect of float*/
  7. }

The code above places a 100% high float that has no width as the first element on the page. This enables Opera to resize nicely and keep the footer where it should be. To negate any possible clearing issues on normal page content we use a negative top margin to pull the float high above the viewport (32767px is Opera’s limit so don’t use any more than this). Even with the negative margin applied Opera still continues to resize the page nicely.

IE8

The last browser to address is IE8 as it won’t play ball with this method although it does understand the pseudo element :before, it doesn’t apply the 100% height to it as required. Therefore we need another fix and again the answer is quite simple and logical and we supply IE8 with height:100% but declare the element as display:table. This will enable the element to be 100% high initially but also to expand if content dictates which is exactly the way that tables work.

As IE8 is the only one that needs this rule we will use conditional comments to supply this rule to it. In order to tidy up we can also remove the IE6 hack we added earlier and put it in the conditional comments as shown below.

  1. <!--[if (lte IE 6)|(gte IE 8)]>
  2. <style type="text/css">
  3. #outer {height:100%;display:table;}
  4. </style>
  5. <![endif]-->

If you are not familiar with the first line of the conditional comments shown above then in English it basically says “If less than or equal to IE6 or If greater than or equal to IE8 then use the code below“.

As IE6 doesn’t understand display:table then it is simply ignored by IE6 and it gets the height:100% rule only. IE8 on the other hand understands both and gets both just as it needed. This saves using two sets of conditional comments and keeps the page nice and tidy.

Here is the full code with comments to make it easier to understand or you can view source from the Full live version.
Full Code

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  5. <title>Sticky Footer at Bottom</title>
  6. <style type="text/css">
  7. * {/* for demo only*/
  8.     margin:0;
  9.     padding:0
  10. }
  11. html, body {
  12.     height:100%;/* needed to base 100% height on something known*/
  13. }
  14. #outer {
  15.     width:760px;
  16.     background:#ffffcc;
  17.     margin:auto;
  18.     min-height:100%;
  19.     margin-top:-40px;/*footer height - this drags the outer 40px up through the top of the monitor */
  20. }
  21. #header {
  22.     background:red;
  23.     border-top:40px solid #fff; /* soak up negative margin and allows header to start at top of page*/
  24. }
  25. #footer {/* footer now sits at bottom of window*/
  26.     background:red;
  27.     width:760px;
  28.     margin:auto;
  29.     height:40px;/* must match negative margin of #outer */
  30.     clear:both;
  31. }
  32. /*Opera Fix*/
  33. body:before {/* thanks to Maleika (Kohoutec)*/
  34.     content:"";
  35.     height:100%;
  36.     float:left;
  37.     width:0;
  38.     margin-top:-32767px;/* thank you Erik J - negate effect of float*/
  39. }
  40. h1,h2,p{padding:0 10px;}
  41. </style>
  42. <!--[if (lte IE 6)|(gte IE 8)]>
  43. <style type="text/css">
  44. #outer {height:100%;display:table;}
  45. </style>
  46. <![endif]-->
  47. </head>
  48. <body>
  49. <div id="outer">
  50.     <div id="header">
  51.         <h1>Sticky Footer</h1>
  52.     </div>
  53.     <h2>Ultimate Sticky footer that works in ie5/6/7/8, Opera 9 and 10, Firefox 2+, Chrome, Safari 3+ (and probably every other modern browser)</h2>
  54.     <p>test</p>
  55.     <p style="clear:both">Element with clear:both added</p>
  56.     <p>test</p>
  57.     <p>test</p>
  58. </div>
  59. <div id="footer"><p>Footer</p></div>
  60. </body>
  61. </html>

Just in case you are thinking that there is too much code overhead in this if we look at the html needed you will see that there aren’t really any extra elements (apart from assuming that you have a header in the page.) The minimum html is as follows.

  1. <div id="outer">
  2.     <div id="header"> </div>
  3.     <p>test</p>
  4. </div>
  5. <div id="footer"> </div>

I don’t think you can get neater than that!

Although I have shown the example in a fixed-width format you can use it in a percentage or fluid width page as long as you take care with the footer dimensions and don’t let the footer height increase with content. To enable text-resizing you could size the footer and negative margins using ems but that would assume you were basing them on the same font-size, etc.

Things to watch out for

Remember to take into account the effect of collapsing margins on the first and last elements in the wrapper because these may have an impact on the position of the parent or the position of the footer.

I also often see problems where authors have tried to make space at the top and bottom of the page by simply moving the wrapper down the page. The result of this is it moves the whole 100% down the page and ruins the effect. Everything has to happen in the 100% high wrapper and if you want space at the top then you would need to reduce the space at the bottom or be creative in other ways.

Here are just a few various examples.
Main Version
Example 1
Example 2
Example 3
Example 4

I hope you have enjoyed this article and if you have any questions then post away and I’ll try my best to answer them.

17 Responses to “CSS – A Sticky Subject”

1 Dan Schulz

Hey Paul, just curious (even though I asked you on Skype, I thought I’d post it here for the record). Why not use a conditional comment that specifically excludes IE7 instead of targeting IE6- and IE8+?

2 Paul OB

Yes I could have done that (and not sure why I didn’t now) :)

I could have done this instead:

etc…..

= if not IE7

3 Paul OB

Where did the code go :)

if !IE 7

4 Erik J

Hi Paul, as you know alredy IE8 does also apply the #outer min-width if a child to it base its height on percent.
In the case a clearer is needed, an :after rule could replace the IE8 table display. As the parent height is auto, the 1% height would be none. Would such rule work reliably for IE8 do you think?

#outer:after {
clear:both;
display:block;
height:1%;
content:””;
}

5 Paul OB

Hi Erik,

Actually that works well for IE8 instead of the display:table so you could use either.

I usually avoid using the :after clearing mechanism on sticky footers because it introduces a space at the bottom in Firefox. However using the negative top margin method as shown above the gap doesn’t seem to appear.

This also raises another interesting point in that adding overflow:hidden to #outer kills the sticky footer effect in Opera.

Therefore to clear floats in the sticky footer version I would now advocate using the :after method instead of using overflow.

So perhaps building this :after method in from the start would be more reliable and avoid using the display:table for IE8 also.

6 Erik J

Hi Paul, You already know IE8 does recalculate the #outer min-height if a child to it base its height on percent.
So in the case a clearer is needed, a :after clearing rule could replace the IE8 table display.

#outer:after {
clear:both;
display:block;
height:1%;
content:””;
}
As the parent height is auto, the 1% height would be none as the content is. Would such rule work reliably for IE8 min-height do you think?

7 Erik J

Sorry for the double post. :)

8 Ray H.

>>(This also raises another interesting point in that adding overflow:hidden to #outer kills the sticky footer effect in Opera.)

Hi Paul,

I had also noticed that overflow:hidden; on the parent div caused problems in Opera.

Opera’s issue along with the option of allowing content to protrude outside of the parent without being cut off was the reason I had preferred the :after float clearing method.

Erik’s solution of height:1% does indeed seem to be a solid fix for IE8 which eliminates the need for any Conditional Comments or IE stylesheets. :)

9 Paul OB

Thanks for the comments Ray and yes I think all areas are covered nicely now with Erik’s extra fix:)

10 Sylwester w Górach

yea and i think that the most populat is the main version :P

Nobudy makes websites like in example 5 now, so i think that you may delete it.

11 mack

but in fact, the meta !important is not encourge to use, because it will complicate the css3 standard, and the hacks is also.

12 Paul OB

@mack – You shouldn’t use !important as a hack and you should also avoid using it in general use but in cases where it’s called for there is no problem in using it and will not cause any problems in css3.:)

!important should be used sparingly otherwise it will negate its usefulness.

13 crusher machine

I usually avoid using the :after clearing mechanism on sticky footers because it introduces a space at the bottom in Firefox. However using the negative top margin method as shown above the gap doesn’t seem to appear.

14 iTechRoom

Demo could have been better to show how it sticks when page scrolls.

15 I want sticky footer - Ste Goodram

[…] O’Brian suggested the addition of display:table for IE 8, as well his sticky footer article here got a nice suggestion for a fix in Opera which we used here as well. It’s been tested in over […]

17 Paul OB

Opera 11 fixes the old redraw issue with the footer but introduces another bug instead and in the sticky footer routine above the main page wrapper must be set to clear:both or it gets dragged upwards in opera11.

Updated example with fix in place:

http://www.pmob.co.uk/temp/sticky-footer-ie8new-no-table.htm

mulberry sale spyder womens jacket cheap new balance 574 mulberry outlet cheap new balance 574 arcteryx outlet mulberry sale spyder womens jacket mulberry sale spyder womens jacket mulberry outlet mulberry outlet new balance 574

Popular Articles

Top 10 Commentators


Subscribe to this feed! Subscribe by Email!

Random Bits Podcast

You need to download the Flash player from Adobe

Blogs Worth Reading