Understanding the CSS3 Flexbox (Flexible Box Layout Module)

| 36 replies | Short URL: http://benfra.in/1y7

This post looks at how to use the CSS3 Flexbox…

Update 17th August 2012: The original version of this post was based on the Editor’s Draft, 20 April 2012. Since then, the specification has undergone further changes. The article has now been updated and is based on the Editor’s Draft, 13th August 2012. The current Editor’s draft can be found at http://dev.w3.org/csswg/css3-flexbox/

One of the challenges compounded by responsive web design is the need to fully decouple markup (HTML) from presentation (CSS). With a single layout, depending upon where we want a piece of markup to appear, we can (worst case) amend the source order to aid. Or, use any number of CSS workarounds to shift elements, visually, where we need them.

However, with a responsive design there are effectively any number of layouts we need to present with a single piece of markup. To exemplify, perhaps we have a navigation list at the top of our design on a wide Desktop canvas but we need it to appear in the footer area for narrower canvases such as Smartphones. At present it’s a pickle and whilst JavaScript often comes to our aid, the folks at the W3C are cooking up a CSS based solution.

Alongside my book, ‘Responsive web design with HTML5 and CSS3’ I’d also written a feature on Responsive Web Design for MacUser magazine. Through that, I’d been privileged enough to quiz some of the web’s best, including Jeremy Keith, Richard Rutter, Ethan Marcotte and Andy Clarke. One thing that kept cropping up when I raised the question of moving elements about on-screen was the CSS Flexible Box Layout module, or ‘Flexbox’ for short…

Flexbox has had a number of versions. 2012 saw a major overhaul (breaking any prior implementations) and since then it has been tweaked time and again. This is both good and bad news. The good: it’s being worked on as I post this. The bad: there’s still hardly any support for the new spec: http://www.w3.org/TR/css3-flexbox/.

However, episode 29 of the ShopTalkShow podcast featured an interview with Tab Atkins, current editor of the Flexbox draft. He is confident that browser implementation is coming. As an example, my current developer build of Chrome (22.0.1229.6 dev) supports the current version of the specification. Even IE10 is aiming to fully support Flexbox when released. If you want to try Flexbox out and open any of the links below, best bet is to download/use Google Canary or use the most recent dev build of Google Chrome.

For the sake of clarity, I wrote the info below using Google Chrome 22.0.1229.6 dev and the spec as of Editor’s Draft, 13th August 2012 – http://dev.w3.org/csswg/css3-flexbox/

However, despite the state of flux that Flexbox is in, don’t let that stop you getting acquainted with it, and the benefits it could potentially offer us. Here’s the W3C intro to the module (as of Editor’s Draft, 13th August 2012):

The specification describes a CSS box model optimized for user interface design. In the flex layout model, the children of a flex container can be laid out in any direction, and can “flex” their sizes, either growing to fill unused space or shrinking to avoid overflowing the parent. Both horizontal and vertical alignment of the children can be easily manipulated. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.

Flexbox provides a new layout mode

CSS 2.1 gave us four layout modes: block, inline, table and positioned. ‘Flexbox’ will work in addition to those. If you’re into responsive web design, this particular piece of the document should pique your interest:

The contents of a flex container…can have their display order reversed or rearranged at the style layer (i.e., visual order can be independent of source and speech order)

More of that shortly, first, let’s consider a simple example: Flexbox basic Gist (remember, this will only work in a recent build of Chrome at present).

The markup is a simple list:


<ul>
  <li>hello</li>
  <li>I am</li>
  <li>Flexbox</li>
  <li>How do you do?</li>
</ul>

And crucially, here’s the CSS:


ul { 
    display: flex;
}
li {
    margin: auto;
}

If you resize the browser window in Chrome you’ll see the spacing flexes. You can achieve a similar effect with CSS tables so you’re probably thinking ‘so what?’ – instead let’s look at something more impressive.

Flexbox allows you to re-order items on screen

Here’s a typical (simplified) document structure: (here’s a Dabblet Gist if you’d rather: http://dabblet.com/gist/3437855).


<header role="banner">here is the header</header>
<div class="wrapper">
  <div class="mainContent">Here is the main content</div>
  <aside role="complementary">here is a sidebar, after the main content (although we want to make it appear before)</aside>
</div>
<footer role="contentInfo">here is the main footer</footer>  

Although I’ve got my content first in the source order, I can apply a float to make the sidebar appear first (on the left). Here’s some basic CSS to illustrate:


body {
background: #bfbfbf;
min-height: 100%;
}
header {
    background-color: blue;
    min-height: 100px; 
}
aside {
    background-color: orange;
    width: 34%;
    min-height: 200px;
    float: left;
}
.mainContent {
    background-color: white;
    width: 66%;
    min-height: 200px;
    float: right;
}
footer {
    background-color: green;
    width: 100%;
    clear: both;
    min-height: 100px; 
}

Usual problems

However, what happens if we want another div to appear before the aside (sidebar)? So the source looks like this:


<header role="banner">here is the header</header>
<div class="wrapper">
  <div class="mainContent">Here is the main content</div>
  <aside role="complementary">here is a sidebar, after the main content (although we want to make it appear before)</aside>
  <div class="extraDiv">Oh Lord, now my head is starting to hurt!</div>
</div>
<footer role="contentInfo">here is the main footer</footer>  

And then the additional styles (note: at this point I’ve amended the width of the content to 60% and aside to 20% too):


.extraDiv {
    background-color: pink;
    width: 20%;
    min-height: 200px;
    float: left;
}

You can view the amended Dabblet/Gist of that here: http://dabblet.com/gist/3437874

The problem is, that even with the floats in place, because the .extraDiv comes after the aside, there is no simple, non-hacky way of fixing the layout issues.

The Flexbox way of (visually) re-ordering content

When browsers fully support Flexbox, you will be able to solve this issue easily…


/**
 * CSS Flexible Layout Module - flexbox styling
 */
body {
    background: #bfbfbf;
}
header,aside,.mainContent,.extraDiv,footer {
    padding: 1em;
    font-size: 1.4em;
}
header {
    background-color: blue;
    padding: 1em;
}
.wrapper {
    display: flex;
}
aside {
    background-color: orange;
    width: 20%;
    order: 2;
}
.mainContent {
    background-color: white;
    flex:1;
    order: 3;
}
.extraDiv {
    background-color: pink;
    width: 20%;
    order: 1;
}
footer {
    background-color: green;
    width: 100%;
    clear: both;
}

Notice, I’ve removed the float declarations (float and clear has no effect on Flexbox items), and removed the width declaration from the .mainContent. I’ve also been able to remove the min-height properties from the aside and .extraDiv. This is because the Flexbox will make the elements in the flexbox an equal height by default. You can see the Dabblet for this stage here: http://dabblet.com/gist/3437877

Also remember this will only render correctly in recent dev build of Chrome or Chrome Canary at present!

The important thing to take from this is this is that I’ve told the containing element I want it to display as a flexbox:

.wrapper {
  display: flex;
}

And then I’ve defined the order for each of the enclosed elements using the order: property. For example:

.extraDiv {
    background-color: pink;
    width: 20%;
    order: 1;
}

Have a go with the Dabblet – you can amend the order each element appears just by adjusting the order: properties – how good is that!

Re-order a whole page with Flexbox

We can take this a step further by adding the flex property to the body tag. That, way, anything can be visually re-ordered (remember, actual source order remains unchanged). For example: http://dabblet.com/gist/3437885. There’s also media query ‘breakpoints’ set at 500px and 400px too so resize the browser window and see how the visual source order varies.

Flexibility

The ‘Flex’ shorthand property enables preferred widths to be set for elements within a Flexbox. Here’s the info: http://dev.w3.org/csswg/css3-flexbox/#flex.

You’ll notice in the prior gist (http://dabblet.com/gist/3437885) that there are no ‘width’ values set. Instead, all the widths are set with the flex: property and then a value. For example, flex: 60%;. If width had explicitly been set, the value of flex would have been used first as the elements are all flex ‘items’ (e.g. display: flex; was set on the body tag). Conversely, setting a flex property/value pair on an item not within a flex container will have no effect.

The percentage part indicates the ‘preferred width’ (where a ‘preferred width’ is absent, the standard width property/value is used) of the element within the flexbox.

However, there is another way to size things with Flex and I admit it took my tiny mind far too long to get my head around this (hopefully I have this right!)…

Take a look at this Gist: http://dabblet.com/gist/3437894

Here is the relevant section of CSS:


aside {
    background-color: orange;
    order: 4;
    flex: 1 ;
}
.mainContent {
    background-color: white;
    order: 3;
    flex: 2;
}
.extraDiv {
    background-color: pink;
    order: 2;
    flex: 1;
}

Ratios

Instead of setting a percentage or pixel value for the flex items, I am using a ‘ratio’ value of the available space. That sounds more complicated that it is in this example. In practice, the .mainContent is merely 2 times bigger than .extraDiv and aside – which both have a ratio of 1 set. You can play with the flex numbers and see how the layout changes.

Flexbox based responsive designs go up to 11

It’s not hard to see that Flexbox will be fantastic for responsive web design. It’s a perfect solution for if you want to switch where the navigation displays at different viewports. For example, I added this to the bottom of the Dabblet:


@media all and (max-width: 500px) {
    footer {
        order: 5;
    }
    header {
        order: 1;
    }
}

Have another look at that Dabblet and re-size the browser viewport below 500px; you’ll see the header and footer switch positions…

For a responsive design, you might make use of extra Flexbox goodness. You can use the flex-flow property and change it to ‘column’ below a certain break-point (and optionally revert/change the source order too if you wanted to as I have done below) to stack everything within the Flexbox on top of one another (rather than in a row as set previously):


@media all and (max-width: 400px) {
    /* change below 400px */
    body { 
        flex-flow: column wrap; 
    }
    .mainContent, aside, footer, header, .extraDiv {
        /* Return them to document order and make all full width*/
        order: 0; 
        flex: 100%;
    }
}

Using Flexbox to solve common alignment problems

One recent addition to the Flexbox spec is the information on alignment (or I missed it last time I looked). This can really help solve some common UI patterns that are far more difficult to achieve consistently than they should be!

Here is my own example of the one used in the W3C spec; a navigation pattern – bunch of links on the left, and an offset link on the right: http://dabblet.com/gist/3379478

Here is the markup:


<header role="banner">
    <ul>
        <li data-icon="f"> Facef*ck</li>
        <li data-icon="t"> Twitter</li>
        <li data-icon="i"> LinkenIn</li>
        <li data-icon="u"> YouTube</li>
        <li data-icon="g"> Google+</li>
        <li class="alternate"> Contact</li>
    </ul>
</header>

Here is the relevant Flexbox CSS:


header ul { 
    display: flex; 
}
li {
    list-style: none;
    margin-left: 0.6em;
}
.alternate {
    margin-left: auto;
    margin-right: 1em;
}

By using margin-left: auto; on the last flex item it automatically offsets it over to the right. Super handy! Plus it saves having to break items up in the source code for the sake of visual layout that should all be together.

Flexbox offers so much more!

I’ve really only scratched the surface of the new Flexbox module here. Check out the full Flexbox specification here. There is heaps of extra functionality including alignment and direction – basically, the aim is for all the annoying layout problems you’ve suffered in the past to be solved by Flexbox. Hopefully someone with a HUGE brain will figure out a good polyfill too, that way we can start using it in production ahead of time. Until then, if you want to create Flexbox layouts, you’ll need to fork your layout code with Modernizr (or similar) and provide a fallback layout based on existing (e.g. Floats) techniques.

About The Author

36 Responses to “Understanding the CSS3 Flexbox (Flexible Box Layout Module)”

  1. Understanding CSS3 Flexbox | Qtiva

    [...] Ben Frain updated his primer on Flexbox to the new standard. Because, you know, the old one is old . Direct Link to Article — Permalink Understanding CSS3 Flexbox is a post from CSS-Tricks Direct Link [...]

  2. SiamKreative

    What a great article! I’m looking forward to using this awesome Flexbox property… it should solve many issues related to CSS grids :) But as you said, we’ll need some kind of fallback.

  3. Chuck Barlow

    Thanks for the update. A few comments:

    1) These also work in the Webkit nightly.

    2) In example 3378779, since you applied flex to only .mainContent and a percentage width to the others, I think it would be better just giving them all percentage widths/flexes. There’s no real advantage to using flex in that way, just saving a few bytes in exchange for clarity.

    3) In the ratio example, it’s not a ratio of available space, anymore (it looks like you correct yourself later). Since you’re applying integer values (flex-grow) to all elements, they’re treated as width ratios (flex-basis). In that case, it makes more sense to me to just use percentages. They’re familiar and easier to keep straight. See the ‘flex: ’ example under http://dev.w3.org/csswg/css3-flexbox/#flex-common

    4) The last example could also be done using “li:nth-last-child(2){flex:1;}”. I’m not crazy about the “margin-left:auto” trick. It just seems unnatural.

    5) Interestingly (and fortunately), pseudo elements honor flexbox properties, including order.

  4. Ben Frain

    Chuck, some good insight there, thanks for that. Hope to go through the spec again next week and make some further additions and tests on the post so I’ll tidy up where relevant then.

  5. John Faulds

    Excited by the possibilities, but also slightly pessimistic about when we’ll be able to use flex box when you consider what will happen in browsers that don’t support it.

    By the way, small typo: aside role=”complimentary” should be ‘complementary’.

  6. Ben Frain

    Hi John, good catch on the typo, thanks. Have updated body copy, will update gists in due course.

    I’m confident that when the draft seems settled enough (which Tab Atkins intimated wasn’t far off) and people start to use flexbox a polyfill of sorts will come along. I’m a believer that necessity is the mother of invention ;)

    Until then, I suppose we could use it in some instances as progressive enhancement and let older browsers get the forked layout (with the aid of Modernizr).

  7. Hank

    Thanks for posting this article. By the way, “exasperated by” is using the term incorrectly. You may want to edit this article. Thanks.

  8. Ben Frain

    Hi Hank, after a brief phone call for adjudication (my mum is a language Nazi) I’ll concede that point.

    I’d meant in the sense of ‘intensifying’ the issue so I will amend to ‘compounded by’.

    Thanks for taking the time, Ben

  9. Matthew Douglas

    It’s articles like this, shared freely, that have given me the knowledge to make a living and feed my family and make my life better, thank you.

  10. Ben Frain

    Cripes Matthew, that’s just about the most positive comment someone could ever have, thanks. We are all in the same merry boat, learning and sharing as we go!

  11. David McCallum

    I am now intrigued by the possibilities of flexbox – thanks for this article.

    Can you also tell me in which issue of MacUser your feature on Responsive Web Design appeared as I must have missed it.

  12. David McCallum

    Now that I know what it looks like I should be able to find it in my pile.

    Unless her indoors has thrown it out!

  13. David McCallum

    Found it! Still in the cellophane.

    25th May 2012 Vol 28 No 11

  14. Ben Frain

    Excellent, hope you like it. By the way, the piece in that issue on Tim Cook is a cracking piece.

  15. Glenn Atkins

    Ben,

    Great write-up and I’m also enjoying your book (with the ladybug on the front.)

    I cannot get those last two blocks of code (that makes the horizontal menu) to work either from DWCS6 to a live site with Chrome 21 nor on Codepen. The background doesn’t render properly (looks like dark gray only) and the menu is vertical and not horizontal.

    Any ideas?

    Thanks.

    Glenn Atkins

  16. Ben Frain

    Hi Glenn,

    Thanks for the comment, and thanks for the book feedback. Always good to know when someone is enjoying it!

    I just opened the last Gist (http://dabblet.com/gist/3379478) in my Dev version of Chrome (Version 23.0.1243.2 dev) and it renders as I would expect (horizontal menu with ‘carbon fibre’ style background gradients courtesy of Lea Verou’s library http://lea.verou.me/css3patterns/).

    If you want to keep the stable version of Chrome you have (as opposed to the Dev version) then try downloading Google Canary, that runs alongside your existing version of Chrome.

    Otherwise, grab the dev build of Chrome – I’ve found it offers the best compromise between latest features but decent stability (never had it crash on me once!).

    You can get both Canary or Dev versions of Chrome here: http://www.chromium.org/getting-involved/dev-channel

    Can you do me a favour and let me know if either of those sort it out for you? Hope to see your review of my book on Amazon once you’ve finished too!

    Thanks again, Ben

  17. Glenn Atkins

    Ben,

    Thanks for the quick turn-around. What’s odd is that the gist works mostly fine in regular chrome 21, Version 23.0.1246.0 canary, and Version 23.0.1243.2 dev-m.

    When I say mostly, it aligns the menu horizontally but on some loads the icon fonts are behind the html text. On other loads it’s perfect.

    But when I try to copy and paste that code into an HTML5 test page in DWCS6 and upload it to a live site, the background only renders solid black and the menu is stacked vertically.

    Very strange indeed and I appreciate your help.

    Thanks.

    Glenn Atkins

  18. Ben Frain

    I’ve not used Dreamweaver for some years but have you checked what Dreamweaver is actually saving compared to the code you are pasting in (e.g. doing a ‘diff’ on the two)? Sounds like it could be truncating the multiple background gradients.

    Does it work if you copy the code into a ‘normal’ text editor like Sublime Text 2?

  19. Glenn Atkins

    OK, I figured it out. What DWCS6 and CodePen both want is this:

    li {
    list-style: none;
    margin-left: 0.6em;
    display: inline;
    }

    Which is simply the addition of “display: inline;” on the li tag. Works perfect in stable Chrome 21.0.1180.83 m and Chrome Canary Version 23.0.1246.0 canary.

    Cheers!

    Glenn Atkins

    Glenn Atkins

  20. Glenn Atkins

    Here’s the full code as adapted from Ben, designed in DWCS6.

    Glenn

    Untitled Document

    Facebook
    Twitter
    LinkedIn
    YouTube
    Google+
    Contact

    @charset “utf-8″;
    /* CSS Document */

    @font-face {
    font-family: ‘IcoMoon';
    src: url(‘http://benfrain.com/downloads/IcoMoon.eot’);
    src: url(‘http://benfrain.com/downloads/IcoMoon.eot?#iefix’) format(‘embedded-opentype’),
    url(‘http://benfrain.com/downloads/IcoMoon.svg#IcoMoon’) format(‘svg’),
    url(‘http://benfrain.com/downloads/IcoMoon.woff’) format(‘woff’),
    url(‘http://benfrain.com/downloads/IcoMoon.ttf’) format(‘truetype’);
    font-weight: normal;
    font-style: normal;
    }

    [data-icon]:before {
    font-family: ‘IcoMoon';
    content: attr(data-icon);
    speak: none;
    }

    /**
    * CSS Flexible Layout Module – flex property */
    * { transition: all 0.2s; box-sizing: border-box; }

    body {
    background:
    radial-gradient(black 15%, transparent 16%) 0 0,
    radial-gradient(black 15%, transparent 16%) 8px 8px,
    radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 0 1px,
    radial-gradient(rgba(255,255,255,.1) 15%, transparent 20%) 8px 9px;
    background-color:#282828;
    background-size:16px 16px;
    width: 100%;
    text-shadow: 2px 2px 0px #000;
    }
    header {
    color: white;
    font-size: 2em;
    }
    header ul {
    display: flex;
    }
    li {
    list-style: none;
    margin-left: 0.6em;
    display: inline;
    }
    .alternate {
    margin-left: auto;
    margin-right: 1em;
    }

  21. Ben Frain

    Hi Glenn, in that case you should be using display: inline-flex;. Does that also work?

    display: inline; is a non Flexbox layout mode (see beginning of the post) so whilst it may solve this problem in isolation I don’t think it would be the way to do it with Flexbox.

    I’ll see how that last example shakes out in future browser iterations and perhaps amend accordingly.

  22. Glenn Atkins

    Very strange indeed. inline-flex; does not work for me on Chrome, FF or Safari, but leaving it at just inline; does.

    I got the idea from your link to the specs in Item 3, but obviously deployed in wrong by just using inline; but it works.

    Any other thoughts?

    Thanks for your help.

    Glenn Atkins

  23. Ben Frain

    Glenn, the display: inline; will work because that’s an existing and widely supported layout mode already implemented across modern browsers (hence you are getting a horizontal layout of links).

    However, at this point I don’t believe it should be necessary to do that when using Flexbox (for example, the specification example only shows ul being set to display: flex;).

    I haven’t needed it and the example works in my build of Chrome (which I know supports this version of the spec draft).

    In all honesty I wouldn’t sweat it too much yet as this whole thing is in a state of ‘flex’ (see what I did there? No? I’ll get my coat…) and until there are a few browsers implementing the draft I would expect to see these kind of anomalies. I trust the other examples render as I described?

    Perhaps I should update with some grabs of what I’m seeing so folks know if they are getting the required result…

  24. Glenn Atkins

    Ben,

    This gist renders fine for me in Chrome 21.0.1180.83 m and in Canary Version 23.0.1247.1 canary, of which both are current as of this post.

    When I put that gist’s code in DWCS6 and either upload to a live site or a local dev environment running XAMPP with Apache/MySQL, I get this:

    hello
    I am
    Flexbox
    How do you do?

    with the bullets in front.

    Here is the code:

    Untitled Document

    hello
    I am
    Flexbox
    How do you do?

    and the css

    @charset “utf-8″;
    /* CSS Document */

    /**
    * Flexbox 2012 basic
    */

    ul {
    display: flex;
    }
    li {
    margin: auto;
    /*margin-left: 6em;
    display: inline;*/
    }

    If I uncomment display: inline; the list spreads horizontally, drops the buttons, and each item is right next to each other. If I comment out margin: auto;, and uncomment margin-left: 6em;, and display: inline;, I get a spaced out horizontal line without bullets.

    Here’s a link using the code from the referenced gist:

    http://garrisonassetmanagement.com/BasicFlexBox2.html

    Weird indeed.

    Thanks.

    Glenn Atkins

  25. Glenn Atkins

    I’m not having much luck posting html code, but you can see it at the link.

    Glenn

  26. Ben Frain

    Hi Glenn, I think the problem with your page could possibly be that you aren’t including vendor-prefixes which Dabblet provides as standard via http://leaverou.github.com/prefixfree

    Try adding the prefix free JS file in the head and see if that helps?

    If you look at my Gist you will also see I have got {css}list-style: none;{/css} on the {html}li{/html} selector – that’s what removes the bullet icon on lists.

  27. Glenn Atkins

    Ben,

    That did it. Added webkit prefix to the display: -webkit-flex; and it works perfectly.

    Hit the link again to see.

    I have used vendor prefixes a million times. Still, a rookie mistake in this case.

    Thanks for sticking with me on this.

    Glenn

×

Comments are closed.

787Days
787 days since this post was last revised. Specific details are likely out of date.
2338 words
MENU