Understanding the CSS3 Flexbox (Flexible Box Layout Module)
This post looks at how to use the 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.
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.