Wednesday 26 March 2014

HTML5: Flexible Box Model

Source: http://www.html5rocks.com/en/tutorials/flexbox/quick/#toc-center

Introduction

I'd bet that you've done your fair share of styling elements to be arranged horizontally or vertically on a page. As of yet, though, CSS has lacked a suitable mechanism for this task. Enter the CSS3 Flexible Box Module, or Flexbox for short.
The draft describes Flexbox as:
[...] a CSS box model optimized for interface design. It provides an additional layout system alongside the ones already in CSS. [CSS21] In this new box model, the children of a box are laid out either horizontally or vertically, and unused space can be assigned to a particular child or distributed among the children by assignment of "flex" to the children that should expand. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions. This model is based on the box model in the XUL user-interface language used for the user interface of many Mozilla-based applications (such as Firefox).
Super. What this means to you, as a developer, nay, as a Layout Architect™ is:
  1. Floats? Where we're going, we don't need floats.
  2. Layouts that were challenging before are more sensible.
  3. We can create true flexible layouts, and the browser will do the calculations for us.
Flexbox gives us a new value for the display property (the box value), and eight new properties:
  • box-orient
  • box-pack
  • box-align
  • box-flex
  • box-flex-group
  • box-ordinal-group
  • box-direction
  • box-lines
Eight new properties? Yeah, a little overwhelming, right? Okay let's break these down.

Common flexbox style properties

Styles used on the box

display: box
This new value for the display style opts this element and its immediate children into the flexible box model. The flexbox model works only with immediate children.
box-orient
Values: horizontal | vertical | inherit
How should the box's children be aligned? There are two additional valuesinline-axis (the real default) and block-axis, but they map to horizontal and vertical respectively.
box-pack
Values: start end | center | justify
Sets the alignment of the box along the box-orient axis. So if box-orient is horizontal it will chose how the box's children are aligned horizontally, and vice-versa.
box-align
Values: start | end | center | baseline | stretch
Basically box-pack's brother property. Sets how the box's children are aligned in the box. If the orientation is horizontal it will decide the alignment vertically and vice-versa.

Styles used on the box's children

box-flex
Values: 0 | Any integer
The flexibility ratio for this child. If a child had 1 and its sibling had 2, any additional space in the parent box would be consumed twice as much by the sibling. It defaults to 0 which is inflexible.
I've left box-flex-groupbox-ordinal-groupbox-directionbox-lines out of the above because, frankly, they're not necessary for most of your flexbox work. View the links in the summary to see those properties at work.
A note on syntax: current implementations of flexbox in WebKit and Gecko require the use of vendor prefixes. In this tutorial, I've mostly left them out for clarity. For the details, read the browser support section.

What is flexibility?

The box-flex style property defines if a child of a flexbox should be flexible or inflexible, and helps to define its flexibility ratio relative to its siblings. Let's demonstrate what that actually means. First, let's start with three boxes.
<div id="flexbox">
  <p>child 1</p>
  <p>child 2</p>
  <p>child 3</p>
</div>
Now, we want them to lay out horizontally next to each other, and their heights should always match, regardless of the content in each. How would you currently tackle this? Most, without thinking, would simply float these paragraphs, perhaps adding overflow:hidden; to the parent in order to clear the floats. Nothing very special. But we could also do it quite easily with flexbox:
#flexbox {
  display: box;
  box-orient: horizontal;
}
In the above code, we’re simply telling the parent to behave according to this flexbox model, and to lay out all its children along the horizontal axis. No floats. Yay.
The widths of the children remain as specified (or their inherent width if not specified). This means that if the total widths of all the children is less than the total width of the parent, we’ll get something like this:

3 child elements retain their inherent widths within the parent element

By default, children of a flexbox remain inflexible. That might seem a little odd, but it's it gives a chance for the children to opt into the flexible experience. But what if you wanted children one and two to have specific widths and child three to adjust itself depending on the available space within the parent? Flexbox to the rescue:
#flexbox {
  display: box;
  box-orient: horizontal;
}
#flexbox > p:nth-child(3) {
  box-flex: 1;
}
Here, we’re telling the last child to become flexible, and to take up available space. Since we’ve only allocated space to one element, it will take up all of the available space:

The 3rd child element, having flex, takes up the available space.

Note that the element only becomes flexible along the orientation axis of the box; in this case the element becomes flexible horizontally.
The value for box-flex is relative. So if we were to make the second and third children flexible:
#flexbox {
  display: box;
  box-orient: horizontal;
}
#flexbox > p:nth-child(2),
#flexbox > p:nth-child(3) {
  box-flex: 1;
}
These would each take up the same amount of available space, in fact dividing the available space equally between them.

2 of the 3 child elements share the available space in the parent element.
Now we could also play with setting the box-flex of children 1, 2, and 3 to be 1,23 respectively and they would then absorb the extra space of their parent in that ratio. But instead, lets put this into action.

A basic example

Check the code and the result below:
Hover over one of the boxes to see the flexible box in action. As your hover target expands, the remaining boxes shrink in size, but the total width always matches the size of the parent box.

There are really only two styles in play here, the display: box kicks things into flexbox mode, and then box-flex: 1 on the children makes them operate in flexible mode, taking up all extra space. The default orientation is horizontal, so in fact the box-orient: horizontal isn't necessary but it's wise to include for clarity and maintainability. The default box-align is stretch which is why these divs take up the full height of their parent box. Not too bad, eh? You can see this technique used for navigation in this flexiblenav demo by Raphael Goetter.
Let's take this up a notch.

Center stage: positioning in the middle

Centering something horizontally and vertically has long been a challenge in CSS. The best CSS-only solution we have is position:absolute; left: 50%; top: 50%; paired with negative left/top margins that equal half of the width/height. And that only flies with explicitly sized elements. Ugh!
James John Malcom wrote in to say there is a way, based on display:table-cell; which is at least 6 years old (Dušan Janovský wrote about it in 2004), to align vertically. James documented the full horizontal & vertical solution here. It even works without explicitly sized elements.
With flexbox we can get there a touch easier:

We don't set box-flex on the child because we don't want it using extra space. We just want it to be in the middle, regardless of it's size. The huge advantage is here, that we don't need to have explicit dimensions in order to center it. We could be doing this with something that's typically block or inline. It just works.

Browser support

So can you use the flexible box model in your work today? Not yet, not if you're targeting all browsers. So far, we haven't seen flexbox land in IE9 or Opera 10.60, but it was in one of the IE9 Platform previews, so it's likely Microsoft will add it at some point.
Caniuse.com gives you the lowdown on current browser support – basically it's Firefox 3+, Safari 3+, and Chrome. If you're doing mobile webkit development, flexbox is a great alternative to your standard layout schemes.
But of course, all this is experimental still, so vendor-prefixes are your friend:
/* i wish it was as easy as this: */

display: box;
box-orient: horizontal;
box-pack: center;
box-align: center;
  
/* but in reality you'll need to do this: */

display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
-webkit-box-align: center;

display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-pack: center;
-moz-box-align: center;

display: box;
box-orient: horizontal;
box-pack: center;
box-align: center;
  
If that sounds like a lot of typing, Alex Russell has distilled all these options down into some useful helper classes.

Summary

Without even considering the other four properties, there are lots of possibilities here. To dig into flexbox farther, check out Isotoma's flexbox demos and the Mozilla Hacks post about flexbox. The W3C Flexbox spec has even more detail if you're brave enough to read through its dense text. Go forth and architect some layouts. Enjoy!

No comments:

Post a Comment