Flexbox — fast track to layout nirvana?
Introduction
HTML and CSS is a great content delivery mechanism in many ways — it is easy to learn, flexible and powerful. One thing however that it has never excelled at is complex layouts. If you want to create a simple typographic essay layout with a floated image or two, then fine, but producing complicated multi column layouts has always been fiddly and hackish, and frustrating to get working consistently and precisely across browsers. We usually tend to abuse floats and other constructs for this purpose, and bugs and rendering differences can really spoil your fun.
To combat this, CSS3 includes a number of modules that exist to made different layout tasks much easier. We've already looked at Multi-column layout and Generated Content for Paged Media in other articles, and now we turn our attention to the Flexible Box Layout Module.
Flexbox, as it's commonly called, is designed to allow us to easily manipulate the layout of a series of child elements in ways that were previously difficult. For example:
- Laying out those elements in a row without having to set explicit widths for each one, and wrapping the child elements if there isn't enough space for them to fit on one line.
- Rapidly changing them to be laid out vertically in a column instead.
- Aligning them to the left, right, center, etc. of the parent container.
- Changing the order in which they are displayed, without altering the markup.
- Setting the amount of space each child takes up as a proportion of the parent's width/height, without worrying about specifying exact widths or worrying about the layout still working if the parent dimensions are set using percentages and the viewport size changes.
Sounds rather useful, no? Let's explore it in more detail.
This article uses the latest Flexbox syntax, currently supported in Opera Mobile 12.1+, Opera 12.5+, Firefox 18+ (partial) and Chrome. Chrome needs -webkit-
prefixes, whereas Opera has supported it without prefixes from the beginning. Firefox has partial support with a -moz-
prefix, and the support is also hidden behind a flag (to enable, go to about:config in the address bar, search for "flexbox", and double click the layout.css.flexbox.enabled
line to set it's value to true). Note that other browsers besides Opera have supported Flexbox since 2009, but using an old, outdated syntax that really shouldn't be used any more. Be sure to keep this in mind when reading and using code from pre-2012 articles about Flexbox - Chris Coyier has more on how to tell if you're reading an obsolete article.
A simple flex example
To get the flex party started, lets consider a simple example to show how easy Flexbox layout is. We will look at a "fat footer" type construct with three child elements, each of which contains fairly typical footer content. Let's go for contact details, important global links, and copyright notice. We want to display these in a horizontal line across the footer, centred vertically, and we want the global links to span twice as much width as the other two children. Today we'd typically do this by floating the child elements, setting a width on them, and adjusting alignment using varying amounts of padding, etc. This is often fiddly and imprecise, without setting fixed values on all the dimensions, which can make things inflexible. But Flexbox can help us out here.
Note: to see my finished example, look at my Flexible fat footer page (Figure 1) — you'll see that I've made a funky little design with a column of content and a footer at the bottom, which stays put via some position: fixed;
magic. The layout is flexible, with the main content column sized as a percentage of the page width, and the footer child elements resize in proportion to that main column. You'll also notice that I've used a few media queries to shift the layout for smaller screen widths.
Getting started with Flexbox
So, how do we get started with Flexbox? Most of the Flexbox properties are applied to the parent container of the elements you want to lay out. To specify that you want to lay out a container as Flexbox, you use a special value of the display
property, like so:
footer {
display: flex;
}
Next, you can use the flex-flow
property to specify that you want your child elements laid out in a row
(the default), or a column
. You can also include the keyword wrap
if you like, to specify that you want the content to wrap onto a new line if the parent container is too narrow to fit all the Flexbox children on one line. In my example, I've set the children of my footer to row wrap
:
footer {
display: flex;
flex-flow: row wrap;
}
The significance of the wrap
keyword will become apparent later on.
Note: flex-flow
is actually a shorthand property, for the two properties flex-direction
(values row
, column
, row-reverse
or column-reverse
; the last two values make your row or column run in the opposite direction) and flex-wrap (values wrap
, no-wrap
or wrap-reverse
.)
Main axis, cross axis
One concept you should get used to when working with Flexbox is that of main axis and cross axis, which work kind of like the X and Y axes, but with differences. The main axis always runs in the direction the flex flow is set to, so horizontal if your flex children are laid out in a row, and vertical if they are laid out in a column. The cross axis runs perpendicular to the main axis. These are illustrated in Figure 2.
Setting alignment of your Flexbox children
Flexbox features a number of ways to help you align your children along the main and cross axis.
align-items across the cross-axis
The first one we'll look at is align-items
, which allows you to align your Flexbox children along the cross axis. The different values are:
flex-start
/baseline
: Makes the start of all items sit flush to the start of the cross axis.flex-end
: Makes the end of all items sit flush to the bottom of the cross axis.center
: Makes the center of all items sit flush to the center of the cross axis.stretch
: Makes the child elements all stretch so that they fill the entire length of the cross axis
These are all pretty self explanatory; to see them in action, go and play with the demo, and adjust the values, or check out Figure 3. For the example, I ended up going with this:
footer {
display: flex;
flex-flow: row wrap;
align-items: stretch;
}
And there we have it — all my items will now always fill up the entire height of their parent container, regardless of how much the width and height adjusts as the viewport size changes. This is simply awesome — how many times in the past have you wanted to give a set of columns an equal height even when they contain a different amount of content, and had to mess around with inflexible solutions like setting an equal height
on them, or using faux-columns?
Note: there is also a property called align-self
, which allows you to set align-items
behaviour on individual flex children. These will override align-items
when set.
justify-content
along the main axis
The other main property in this category that will get used a lot is justify-content
, which specified how items are arranged along the main axis, in terms of what happens to the excess whitespace between the children. This property doesn't have any effect when you've set your children and their margins to take up all the available space across the main axis, as is the case in my main example. Therefore I've created another example to demonstrate usage of justify-content
— please take a look at the fixed-width Flexbox example.
In this example I've set the different Flexbox children to take up a set percentage of the width of the main axis:
#first {
width: 25%;
}
#second {
width: 40%;
}
#third {
width: 25%;
}
Then on the parent container I've set a value of justify-content
as follows:
footer {
display: flex;
flex-flow: row wrap;
align-items: stretch;
justify-content: space-around;
}
This value works rather nicely — sharing out all the space in-between the child elements and on the outside of them all in equal amounts. The other available values are as follows:
flex-start
: Bunches all the Flexbox children up at the start of the main axis, with the space all at the end.flex-end
: Bunches all the Flexbox children up at the end of the main axis, with the space all at the start.center
: Bunches all the Flexbox children in the middle of the main axis, with the space equally divided at each end.space-between
: Has a very similar effect tospace-around
, but with no space given to each end of the series of Flexbox children.
Figure 4 shows the effect of different settings of justify-content
:
align-content
for aligning multi-line Flexboxes
You can also specify how spare space between multiple lines of Flexbox children will be distributed, in the case of multiple-line Flexboxes, i.e. where the wrap
keyword has been used. It only really has an effect when your Flexbox children take up a fixed amount of space in the direction of the cross axis, so a fixed height in the case of rows. The available values are:
flex-start
: Bunches all the Flexbox children up at the start of the cross axis, with the space all at the end.flex-end
: Bunches all the Flexbox children up at the end of the cross axis, with the space all at the start.center
: Bunches all the Flexbox children in the middle of the cross axis, with the space equally divided at each end.space-between
: Shares out all the space in between the child elements and on the outside of them along the cross axis all in equal amounts.space-around
: Has a very similar effect tospace-between
, but with less space given to each end of the series of Flexbox children.
Altering the element layout order
Traditionally it has always been a pain to change the order in which elements are displayed without messing around with the source order. Not with Flexbox. Flexbox allows you to set the order
property on child elements to state how soon they will appear in the Flexbox row or column. This property takes a integer value — called an ordinal group — and the higher the ordinal group, the later the child will appear. So for example, going back to my Flexible fat footer example, the box of links is the second child element by default, as shown in Figure 5.
By default, all the Flexbox children are in ordinal group 0. We can easily change this order by giving the children different ordinal group values. Higher values will appear later in the list of flex children; the order of children in the same ordinal group will always be governed by source order. So in my example, I've made the links appear last by setting their ordinal group to 1 (see Figure 6 for the result):
#second {
order: 1;
}
Note: You can use negative order
values.
Making your elements flex
Probably the most powerful feature of Flexbox is the ability to set the length of the child elements in the flex-flow direction (so width
if flex-flow
is row
, or height
if flex-flow
is column
) to be a flexible amount that changes depending on the amount of space available in the parent element in that direction. This is done using the flex
property, whose value can take a total of three parts. Let's add them in one by one and look at the effect they have. First of all, a "flex grow factor":
#first {
flex: 1;
}
#second {
flex: 1;
}
#third {
flex: 1;
}
These are unitless values that serve as a proportion — they dictate what amount of the available space inside the parent each child should take up. If each one is set to 1, every child will set to an equal size inside the Flexbox. If you were to give one of the children a value of 2, that child would take up twice as much space as the others:
#first {
flex: 1;
}
#second {
flex: 2;
}
#third {
flex: 1;
}
You can also set a flex basis value for each child, like this:
#first {
flex: 1 200px;
}
#second {
flex: 2 300px;
}
#third {
flex: 1 250px;
}
First of all, the flex basis values are applied to each child as widths/heights, depending on what direction the flex is flowing. Then, the remaining space left inside the parent element is divided up according to the flex grow factors and given to the children to make their final width. So the children would be sized along the main axis as 200 pixels, 300 pixels and 250 pixels, which is a total of 750 pixels. If the parent container was 950 pixels along the main axis, then there would be an additional 200 pixels of space to distribute between the children. The first and third children would be given 50 pixels each, as they have a flex grow factor of 1. Their final size would be 250 pixel and 300 pixels respectively. The second child would be given 100 pixels, as it has a flex grow factor of 2. Its final size would be 400 pixels.
The third part of the flex
value is rarely used, but let's look at it just in case. You can also give your child elements a "flex shrink factor", like this:
#first {
flex: 1 1 400px;
}
#second {
flex: 2 3 600px;
}
#third {
flex: 1 2 400px;
}
The flex shrink factors, despite their name, are still positive values — the second unitless values in the above declarations. These only come into play when the children overflow their parent container in the main axis direction. They also act as proportion values, but this time they specify the proportion of the "overflow amount" (the amount the children overflow their container by) that will be deducted off the size of each child, to bring the overall size down to equal the size of the parent — in effect, to stop the overflow.
Let's say that the parent container is 1100 pixels along the main axis. This being the case, our above children would overflow it by 300 pixels (they equal 1400 pixels along the main axis, in total). Because of the flex shrink factors set on them:
- The first child would get 1/6th of the overflow amount removed from it, which is 50 pixels. Its computed value would therefore be 350 pixels.
- The second child would get 3/6th of the overflow amount removed from it, which is 150 pixels. Its computed value would therefore be 450 pixels.
- The third child would get 2/6th of the overflow amount removed from it, which is 100 pixels. Its computed value would therefore be 300 pixels.
So a higher flex shrink factor actually results in a smaller element!
The values I ended up with for my main example are as follows:
#first {
flex: 1 0 7rem;
}
#second {
order: 1;
flex: 2 0 8rem;
}
#third {
flex: 1.5 0 7rem;
}
Note: the flex
property is shorthand — you can use the flex-grow
, flex-shrink
and flex-basis
properties to set the three components individually.
Flexbox: responsive advantages
One thing that has worked to really good effect on my example is combining a multiline Flexbox (flex-flow: row wrap
) with a preferred flex length for the child elements (e.g. flex: 1 0 7rem;
), and media queries. At narrower viewport widths, the preferred width cannot be reached without overflowing the parent element, therefore the Flexbox children will wrap onto multiple lines to keep things looking smooth, as shown in Figure 7.
flex: auto and flex:initial
Flex has some other useful values, described in the part of the spec titled Common values of 'flex'. Two of the most useful are auto
and initial. Setting flex: auto;
on a child element of a flexible box (equivalent to flex: 1 1 auto
) will make it fully flexible and size it according to any width
/height
or min
/max
-width
/height
properties it has set; it will expand to take up a proportion of any free space available, but then shrink to fit its contents when there is no extra free space. This can have some interesting effects when combined with a min-width
, say. Take a look at my Flex auto example. In this example, the parent container is set to flex-flow: row
, and the third child container is set to flex: auto;
, and has a min-width
. It therefore expands to fill any excess space on the line, no matter where the toolbar is multiple line or not, and then shrinks neatly as it gets smaller, allowing the child buttons to rearrange themselves to suit.
Try changing the flex: auto;
value to flex: initial
(equivalent to flex: 0 1 auto
) and you'll see that the third child container no longer increases in size when there is excess space, but it still shrinks if needed.
All of this power and flexibility with such little code is definitely a good thing!
Degrading gracefully
Flexbox is a whole new layout model, and browsers that don't understand it will simply ignore it. That might appear to be a deal-breaker that prevents you from ever using it. However, it doesn't need to be. For example, you might use floats or CSS tables to layout the "desktop" layout for a site, and choose to use Flexbox for very narrow (mobile) browser widths, perhaps to move a left-hand nav below the main content.
If you set all the page elements (nav, header, footer, etc) to display:block;
as a default before the Flexbox rules, a browser which supports media queries but not flexbox would simply linearise your content into blocks that span the full width of the device. They would remain in source order, but all the content remains accessible to the user.
Alternatively, CSS tables can be used to pull one element out of source order and put it at the start or end of the content - see the filthy table-caption hack and chuckle as you recoil.
Summary
I hope that this article has made it clear to you exactly how awesome and useful Flexbox is, allowing us to do with away with repeated float and clear abuse, and a number of other horrible, hacky, inflexible layout techniques. You can also see how great Flexbox is for responsive web design when combined with other technologies such as Media Queries.
Note: cover image by Horia Varlan.
This article is licensed under a Creative Commons Attribution 3.0 Unported license.
Comments
JanGen
Tuesday, October 9, 2012
Nice to see this biggest improvement in CSS ever (IMHO) coming to Opera so soon.
Your example (Flexible fat footer) don't work in my latest Chromium Version 20.0.1132.47 Ubuntu 12.04
What version are you talking about?
In Opera Mobile 12.1 it works fine in portrait mode, in landscape mode it's buggy, sometimes the footer is moving, sometimes it's not showing. (after some scrolling), sometimes it's covering text. Samsung Galaxy S
Charlie Clark
Tuesday, October 9, 2012
One thing even if it's possibly self-explanatory but struck me when I looked at the source browser-prefixing for display is on "flex" and not display. Thus display: -o-flex;
Mariusz Kujawski
Wednesday, October 10, 2012
Dodo87
Wednesday, October 10, 2012
A remaining question: You did not tell anything about vendor prefixes. You don't need -o- for Opera, do you? (Why did you use it?) What about other browsers, i.e. Chrome?
(It seems that you used an outdated syntax for linear gradients without the "to" keyword. Latest Opera supports the new syntax, unprefixed.)
JanGen
Wednesday, October 10, 2012
apparently that version hasn't made his way to Ubuntu yet, according to the Stable Release Updates Micro Release Exceptions it should/will.
Chris Mills
Thursday, October 11, 2012
@Charlie Clark yeah, the prefix is on the property value in that case, as it is the value that is experimental, not the property itself.
@Dodo87 Yes! My gradients are out of date, good spot! And I didn't know if we needed prefixes for Opera on the flexbox stuff, or not. When I was writing this, the test builds I was using at the time required the prefix. I'll do some code updating when I get the chance.
Chris Mills
Thursday, October 11, 2012
Martin Kadlec
Friday, October 12, 2012
JanGen
Monday, October 15, 2012
I've uploaded a screenshot. of the problem.
Now PPK decided ;) that there is a default for position:fixed for mobile, I hope Opera will implement this too.
I'm not really convinced with Opera's solution now - scrolling it into view - the problems I encounter with your example seem related to position:fixed AFAICS.
BTW the screenshot is made today, so after your fixes.
olijp
Saturday, October 27, 2012
To follow up on your Oct 11th comment, do any released versions of Opera use the `-o-` prefix with Flexbox? From what I can see Opera Next 12.10 *only* supports the unprefixed version, and no other released version of Opera that I know of (the current Opera 12.02 or the bizarrely numbered Opera Next 12.50) support Flexbox prefixed or not. It seems like Opera is rockin’ unprefixed from the get-go, and if so it’d be better to remove the prefixes from your example and state this in the article.
Cheers, Oli
Martin Kadlec
Tuesday, October 30, 2012
http://dev.operacesky.net/flexbox
torazaburo
Thursday, January 17, 2013
That's wrong. This value determines in what proportion excess width is allocated to the flex items.
Andreas Bovens
Monday, February 11, 2013
Jane Derleenka
Thursday, March 7, 2013