UPDATE: This page is still good, but I recommend this course on flexbox I've put together. It includes an updated version of this page plus more videos and a cheat sheet.
Laying out a design with CSS is fraught with peril of cross-browser bugs and mysteriously collapsing margins. But new tools like flexbox have transformed this once odious task into something you shouldn't dread. By the end of this chapter you'll be coding up new layouts for your design faster than you can think of them.
Remember those old cartoons with the road runner and the coyote? Rocket propelled rollerskates, bottled lightning, bundles of dynamite, magnetic bird seed, razor boomerangs, "super" bombs and giant trampolines -- no technology could catch the bird. Every tool backfired and sent Wile E. Coyote plummeting to hillarious death.
Using CSS to layout your design is like trying to catch the road runner with a jet-powered pogo stick. It won't end well.
Nothing is more frustrating than writing a pile of code, refreshing your browser and seeing a jumbled mess of overlapping text and images. You can't use tables because they aren't semantic. Floats and clears never do what you expect. Just to center something on the page requires strange incantations of auto margins or text-align -- the center tag is right out.
But you know what? This is old news because CSS doesn't suck at layout anymore.
Once you spend some time with flexbox you'll feel like an unstoppable super hero of CSS. Your footer will stay at the bottom of the page where it belongs. Your LIs will flow effortlessly into neat little columns. Divs will shrink and grow and fly in delicate formations like the Blue Angels. Image tags will dance and jump and sing songs of your design prowess.
"Wait, let's not get carried away" you say, "Flexbox is too new. You can't even use it in IE9."
It doesn't matter. Think about it: how many browsers does a PSD work in? None? Exactly. As a designer you're not building the final version of the design. You don't care if flexbox doesn't work in Opera Mini because the comp you are creating for your client only needs to run in Chrome. Worry about SeaMonkey later.
You only need to know a handful of CSS properties to use flexbox. We'll start with an overview and get some terminology out of the way.
Everything starts with the flex container. Once an element is set as a flex container its children follow the flexbox rules for layout instead of the standard block, inline and inline-block rules. Within a flex container items line up on the "main axis" and the main axis can either be horizontal or vertical so you can arrange items into columns or rows.
There is another axis perpendicular to the main axis called the cross axis. You can shift items along both axes. In the examples you've seen so far they have been centered but you can also move them to the beginning or end of either axis.
You can define the size of flex items. This lets you create layouts with, for example, a column with a fixed width and a column that grows or shrinks with the space available.
I've avoided showing you code up to this point because the CSS property names don't map well to the very simple concepts behind flexbox. To help you out I'm introducing the code in a screencast so you can watch what happens to the layout live as I type. I've also created a cheat sheet you can use while you get used to the terminology.
Flexbox doesn't solve every layout problem but it certainly fixes the worst of them. As we look at a few real world examples you will learn a few other techniques for arranging elements on the page but flexbox alone will get you 90% of the way most of the time.
You're looking at three divs centered horizontally — certainly not the most complicated layout of all time. Even so, before flexbox creating this kind of layout required some thought; centering elements on the page was not intuitive.
<div class="products"> <div> <img src="basecamp.png"></img> <h2><a href="">Basecamp</a></h2> <h3>Manage Projects</h3> <p>Used by millions for product management.</p> </div> <div> <img src="highrise.png"></img> <h2><a href="">Highrise</a></h2> <h3><a href="">Manage Contacts</a></h3> <p>Know the people you do business with.</p> </div> <div> <img src="campfire.png"></img> <h2><a href="">Campfire</a></h2> <h3>Work in Real-Time</h3> <p>Group chat rooms for your business.</p> </div> </div>
This is how the markup renders with just enough CSS to make the styling look about the same. You are looking at the default browser layout before we've applied any flexbox rules. The three boxes are div elements. Div elements are block elements and block elements stack, they don't flow next to each other.
First let's center it on the page and get everything flowing into a row.
.products{ display: flex; flex-flow: row; justify-content: center; }
The div wrapping the product divs was set to "display: flex" to make it a flex container. Then to get its items into a row I set the flex-flow property to "row".
Getting everything centered was easy. Justify-content let's you move flex items on the main axis. In this case the main axis is horizontal because the flex-flow property is set to "row". When the main axis is horizontal, justify-content will move the items to the left and right. If the main axis is vertical, (set flex-flow to column), justify-content moves the items up and down instead of left and right. To center the product divs in the example horizontally I set justify-content to "center".
The first real world example is almost done. The middle container needs some space around it and all of the items in each container should be centered horizontally.
To center the items in the div it's easiest to think about them as flowing in a column and then centering them on the cross axis. So in this case the main axis is vertical and the cross axis is horizontal. Here's the code:
/* 1. Make all of the product divs flex containers. 2. Set their items to flow into a column. 3. Center them left-to-right by centering them on the cross axis using align-items. */ .products > div{ display: flex; flex-flow: column; align-items: center; } /* Using selectors like this is covered in another chapter.*/ .products > div:nth-child(2){ margin: 0 2em; }
<div class="tweet"> <div class="content"> <img src="aaron.png"></img> <h2>Aaron Gustafson <span>@AaronGustafson</span></h2> <time>12h</time> <p> Smartphone Browser localStorage is up to 5x Faster than Native Cache <a href="http://www.mobify.com/blog/smartphone-localstorage-outperforms-browser-cache/">mobify.com/blog/smartphon ...</a> </p> <ul class="actions"> <li><a href="">Collapse</a></li> <li><a href="">Reply</a></li> <li><a href="">Retweet</a></li> <li><a href="">Favorite</a></li> <li><a href="">More</a></li> </ul> <ul class="meta"> <li class="retweets"> <span>7</span> <span>RETWEETS</span> </li> <li class="favorites"> <span>5</span> <span>FAVORITES</span> </li> <li class="people"> <ul class="people"> <li> <img src="people1.jpg"/> </li> <li> <img src="people2.jpg"/> </li> <li> <img src="people3.jpg"/> </li> <li> <img src="people4.jpg"/> </li> <li> <img src="people5.jpg"/> </li> <li> <img src="people6.jpg"/> </li> <li> <img src="people7.jpg"/> </li> </ul> </li> </ul> <time>4:35 AM - 24 May 13</time> </div> <div class="reply"> <div contenteditable="true"> Reply to <span>@AaronGustafson</span> </div> </div> </div>
When confronted with a confounding layout problem a common pitfall is to waste time finding the "best" solution. As soon as you find yourself pondering the merits of changing the markup and worrying about messing up the semantics, stop what you're doing and see if absolute positioning would work.
Whenever you're thinking about technical decisions you're not thinking about design. Remember, your job is to create a comp, not the final site. The code you are writing will be reworked and rewritten so don't spend any time making it perfect -- perfect is the enemy of done.
The first challenge with the tweet is the user profile image and the date tag. Although not obvious in this example the tweet text flows under the date and the profile image seems to have its own column. The way the markup was written does not make a flexbox layout immediately obvious. When I put this example together I wasted a few minutes thinking about ways I could change the markup and get the layout I wanted, but then I caught myself and just absolutely positioned both elements where I wanted them.
/* create the space on the left for the profile image*/ .tweet .content, .tweet .reply{ padding-left: 70px; } /*move the image to the left into the column*/ .content > img{ position: absolute; margin-left: -60px; } /* Move the timestamp up and to the right I use "em" instead of pixels here, but pixels are just fine. */ .content > time:first-of-type{ position: absolute; margin-top: -1.5em; margin-left: 30em; } /*I added some vertical padding to all of the elements in the content div. */ .content > *{ padding: .25em 0; }
The next step is to take the actions, (e.g. collapse, reply, retweet, etc.), the retweets and favorites and the list of small profile pictures and put them into rows instead of columns.
.actions, li.people, .meta, .people > ul { display: flex; }
Elements in containers are arranged into rows by default so you just need to set the container to display: flex.
You'll notice in the real tweet the numbers are on top of "favorites" and "retweets". There are two ways to do this. The first is to treat them like flex containers and put them into a column. The second way is to make each of the spans wrapping the numbers into a block element instead of inline.
The second way is only obvious if you know that block elements stack. The beauty of flexbox is you don't need to know the ins and outs of block, inline or inline-block elements. Learn the basics but don't waste time — use flexbox when it's not obvious.
.meta .retweets, .meta .favorites{ display: flex; flex-flow: column; }
I created a third, much more complicated example for you to look at. I recreated most of the CSS-Tricks forum using flexbox for layout.
Use the skills you learned in the chapter on using Chrome's Developer tools to see how some of the elements in the example are laid out using flexbox.