Sticky footers, flexbox and IE10

I recently came across Philip Walton's excellent ”Solved by Flexbox” site, where he shows some good examples of using flexbox to create common layout patterns, some of which have been really tricky to solve with CSS until now. Since there's multiple versions of the flexbox syntax, the examples on Philip's site are (quite sensibly) only focusing on the latest, more stable version. When I tried out the example for ”sticky footer” with all of the different versions of the flexbox syntax, I found that it didn't work in IE10. This post explores how to fix that, as well as make it work in all other browsers that support old and new flexbox syntax.

Update may 17 2014 IE11 broke the demo below, but it’s fixed now. Explanation in the article.

"Sticky footer" is a pattern where we want the footer to stay at the bottom of the visible page unless the content of the page is tall enough to push it down. Expressed in a different way, we want our "main content" to always be at least tall enough to push the footer down to "stick" to the bottom of the screen. Previously, we needed either some rather unnecessary extra elements and weird nesting to achieve that, or possibly using table display properties on top-level items in your markup, which feels icky. Flexbox makes it easier.

First, we assume a very straight-forward markup inside our <html> document, <head> and <html> elements omitted for brevity:

<body>
   <header role="banner">
       <h1>My important site</h1>
    </header>
    <main role="main">
        <h2>Very important stuff about things</h2>
        <p>[...]</p>
    </main>
    <footer role="contentinfo">
        <p>Contact me: <a href="#">edgar@example.com</a></p>
    </footer>
</body>

To do this, we need to give our container (in our case, the <body>-element) a couple of properties.

  1. It needs to become a flex container, so that we can tell its child elements how to flex.
  2. It needs to have a vertical flex axis, so that flexible boxes stack in that direction (horizontal is default).
  3. It needs to be at least large enough to cover the visible viewport.

We end up with a first version of rules for the <body> looking like this:

body {
    /**
    * 1. Making the <body> element behave like a flexbox container.
    * 2. Making flex children line up vertically (horizontal is default)
    * 3. Setting the min-height to 100% of the viewport height.
    */

    /* [1] */
    /* Ye Olde Flexbox for Webkit */
    display: -webkit-box;
    /* Tweener flexbox for IE10 */
    display: -ms-flexbox;
    /* Prefixed "new flexbox" */
    display: -webkit-flex;
    /* unprefixed standard "new flexbox" version for the rest */
    display: flex;

    /* [2] */
    /* Your grandparents flexbox for old Webkit */
    -webkit-box-orient: vertical;
    /* Prefixed new syntax: */
    -webkit-flex-direction: column;
    /* (Same syntax for weird IE tweener) */
    -ms-flex-direction: column;
    /* unprefixed new syntax */
    flex-direction: column;

    /* [3] */
    min-height: 100vh;
}

(It may look like it’s a whole lot of code, but it’s mostly comments. The actual code is 9 lines, I believe.)

The next step is to tell the main section to be tall enough to fill up at least the space down to the footer. This is done by telling it flex: 1, which in this case is a shorthand property (<code>flex</code>) for saying "give me at least all of the space that’s left of <body> when the other elements have had theirs.".

Using all of the different syntaxes and prefixes, we get the following:

main {
    /* Pepperidge Farm remembers. */
    -webkit-box-flex: 1;
    /* IE10, ugh */
    -ms-flex: 1;
    /* Unprefixed */
    flex: 1;
    /* prefixed webkit syntax */
    -webkit-flex: 1;
}

Ok, that should do it, right? Testing in Chrome (latest), yup, cool, Safari (6), yup, cool, Opera (latest), yup, cool, Firefox (latest), yup, cool, IE10... IE10? Turns out, our old Redmond pal is not quite up to snuff. Surprise! IE acts as if the <body> and <html> are only as tall as the content, negating the effect of our rules.

After some experimentation, it turns out that IE10 needs an explicitly declared height on both the <body> and <html> elements to trigger correct calculation of heights for flexible containers along the vertical axis of the page. I haven’t found any other properties that trigger the correct behavior so far. It doesn’t seem to matter which height you set, but 100% seems sensible.

Now we run into the next problem: applying this breaks the layout in other flexbox-capable browsers, making the footer overlap tall content, acting sort of like position: fixed to the bottom. Uh-oh. This is sort of a dead end.

What to do? Well, in my mind, the sticky footer design is usually not a deal-breaker: it’s more of a nice to have. So if we’re happy if just most of our users get the nicely snug-to-the-bottom footer on short pages/wide screens etc, we can use some progressive enhancement to serve the styles only to IE10. Enter Modernizr - we can have it detect the IE flexbox syntax support and narrow the styles down to only to that. Sadly, it doesn’t do that by default in version 2 (IE10 reports as supporting flexbox, but not supporting the old syntax. Twilight zone.).

We need to add a test to Modernizr (via a javascript snippet, for example in the head of the page) that only targets properties the IE10 version, which I found in a thread on this very issue on the Github page for Modernizr:

Modernizr.addTest(’flexboxtweener’, Modernizr.testAllProps(’flexAlign’, ’end’, true));

Now we have a way of targeting IE10: using the .flexboxtweener classname. So we add a new rule to the code example:

.flexboxtweener, .flexboxtweener body {
  height: 100%;
}

That ought to do it, it’s nicely snapping to the bottom of the page! Except now, we have the exact same problem in IE that we had in the other browsers. Shitburgers.

After further examination, it turns out IE10 has a default value for the -ms-flex-preferred-size value (called flex-basis in the newer syntax) that is set to 0px instead of the auto value that it should. So the shorthand we used (-ms-flex: 1;) doesn’t override this, and needs to be set to -ms-flex: 1 0 auto; instead. Easy peasy!

UPDATE: IE11 was released just after this post was published. The demo broke, and I assumed that there was some overlap between the old and the new syntax that meant that further Modernizr detection was necessary, to separate between IE10 and IE11, and left it alone.

It turns out that the only fix needed was to explicitly set the flex-shrink property to 0 and flex-basis to auto in the shorthand for the unprefixed property as well, and all modern browsers as well as IE11 will work!

The final rule set for <main> will look something like this:

main {
    /* Pepperidge Farm remembers. */
    -webkit-box-flex: 1 0 auto;
    /* IE10, ugh */
    -ms-flex: 1 0 auto;
    /* Unprefixed */
    flex: 1 0 auto;
    /* prefixed webkit syntax */
    -webkit-flex: 1 0 auto;
}

The changes shouldn’t influence older webkit browsers, but if I find anything, I’ll update further.

I’ve updated the JS Bin link to the latest version.

Final example available on JSbin.

There! All done. Now we have a working implementation of the sticky footer pattern across all browsers supporting any version of the flexbox syntax, with a little help from Modernizr. Phew.

Following the exploration and adoption of flexbox, both among browsers and web developers, is really interesting: it’s sort of the first real UI layout system for CSS, given that floats, table-layout and positioning have all been either purely hacks for generating layouts, or (in the case of positioning) some times insufficiently flexible for creating the sorts of layouts that we want. I hope we’ll see more of these kinds of pattern sites pop up in the next months and years.

(Footnote: there was an implementation of the old flexbox syntax in Firefox 18 & 19, using the -moz-prefix. However, these were behind a preference flag, and the unprefixed version was shipped with version 20. Hence, there is little need for the -moz-prefix in flexbox code.)