CSS3 Layouts: The Flexible Box Model Basics

June 14th, 2010 by Mike Wilcox

At my employer, BetterVideo, I’m working on an HTML5 video player. Its primary target will be the iPad, and have secondary targets of Safari and Chrome. You can track my progress on this series as I write about interesting findings and bug reports.

The current task is laying out the video controls for the HTML5 player. There is a container box and within that four child boxes. The first is the Play button, then the Slider, a Fullscreen button and finally the Volume button. The trick is to set the three buttons to a fixed width, while allowing the slider box area to be stretchy and fill up the remaining space.

You can create this layout with an HTML table, in fact the Flex Box Model we are about to implement emulates a table’s abilities. But there are two problems with a table, the first being that you need to use a lot of extra JavaScript logic to lay it out if there is something like an extra row. Secondly, there’s no room for customization in CSS. Our player needs to be able to handle different themes for different clients. One may not have a Volume button, it might be a horizontal slider. Another may not want the Fullscreen button. Or they may want these buttons in a different order. And using a table for Webkit on the iPad? One should be banished to an eternity of staring at nested blink tags.

Making this work in vanilla CSS is just plain hard. You have to mess with floats, and if you float:right you’ll reverse the order of the HTML elements. And while I can get that slider area stretchy, at the very least, I’d need to know how many buttons are on either side, and what their widths are, plus their border sizes, paddings and margins. I’d have to add this up with a calculator and hard-code it. And if I have another layout I pretty much need to make a whole slew of styles to accommodate each. But I don’t need to explain. We all know CSS sucks.

The Code

So here is our HTML and basic CSS:

#container {
    width:50%;
    height:30px;
    border:1px solid #ccc;
}
<div id="container">
	<div id="s1">Play</div>
	<div id="s2">Slider</div>
	<div id="s3">FS</div>
	<div id="s4">VOL</div>
</div>

The first step is to make the container flexible. Because my target is Webkit, I’ll show my examples using -webkit, but they all work in Firefox if you use the -moz CSS prefix.

#container {
    display: -webkit-box;
}

Wow, we already have a lot going on. Using display:box also sets up two default properties, box-orient:horizontal, which is why our boxes are no longer stacked, and even cooler, did you notice that the child boxes are filling the container vertically now? The other noticeable default is box-align:stretch. This property fills the space of the “opposite orientation”. We are set to horizontal, so this fills the vertical space. Two other options would be start or end, which would not change their heights, it would line up all the boxes either on the top or bottom (for a horizontal orientation)

NOTE: The default is actually box-orient:inline-axis, but this appears to do the same as horizontal. block-axis looks to do the same as vertical. I’m sure there are differences, but I have not found a scenario that exploits them.

Before we set up our layout the way it will look, I want to show the box-pack property. You could think of it as a text-align property (and they probably would have used that but it was taken!). The options are start, end, center, justify, and it defaults to start.  The previous example has the box-pack defaulted to start.  The other examples are shown below.

-webkit-box-pack:end;
-webkit-box-pack:center;
-webkit-box-pack:justify;

But the more useful feature is the ability to stretch these boxes horizontally and take up the space we wish. Essentially, one of the harder things to do with CSS is to mix pixels and percentages, say that you want one box to be 40 pixels, and another box to be 100% of what remains. That’s exactly what box-flex does. In this next example, we remove box-pack from the container, and add the box-flex property to the child that we want to occupy the remaining space.

#s2 {
    -webkit-box-flex:1;
}

Now that’s what I’m talking about: more control over my layout without messy tables or buggy floats!

A little more detail of box-flex is in order. I used the number “1″ as the value. This is a ratio. All child ratios are applied against the highest one used, or “weighted”. So if we wanted Play to take up 25% of the remaining space and Slider to take up 75% of the remaining space, the CSS would look like this:

#s1 {
    -webkit-box-flex:1;
}
#s2 {
    -webkit-box-flex:4;
}

NOTE: Those ratios are confusing. Using two fours would make them both 50%. You pretty much need to play around until it gives you the sizes you are looking for.

We’ve already accomplished our goal of a stretchy UI, but I want to show one more property that gives the flexible box model the edge over tables. The box-ordinal-group property will reorder our children for us. Say I have a backwards client who wants their UI in reverse with the Play button on the right, and the FS button first:  Or perhaps you want to make internationalization easier where cancel and save buttons might be ordered differently.

#s1 {
    -webkit-box-ordinal-group: 4;
}
#s2 {
    -webkit-box-flex:1;
    -webkit-box-ordinal-group: 3;
}
#s3 {
    -webkit-box-ordinal-group: 2;
}
#s4 {
    -webkit-box-ordinal-group: 1;
}

Let’s see tables do that! I could have also simply reversed the order with box-direction:reverse.

Conclusion

Layouts in CSS has finally taken a step forward. The Flexible Box Model allows you to create complex layouts without the need of buggy floats, verbose tables, or ad hoc frameworks. This feature even supports multiple lines, although we did not go into it, since we just covered the basics of display:box, box-align, box-flex, and box-ordinal-group. To keep the article simple, we only covered horizontal orientation (box-orient),  because that was the use case. But as we all know, organizing our layouts both horizontally and vertically both have distinct challenges in CSS, and The Flexible Box Model comes a long way toward addressing most of them. That elusive calendar layout has just become easier.

How about you? Have you had any non-IE opportunities to use CSS3 layout abilities such as this?

Tags: , , ,

6 Responses to “CSS3 Layouts: The Flexible Box Model Basics”

  1. Chris says:

    Hahaha! I am working on the same thing for work.

  2. kamsieq says:

    jee, this sounds great :] but I hate all this -moz-/-webkit- eventually it turns out that developers need to put doubled css style cos no provider wants to clean those names

  3. Mike Wilcox says:

    Good call on that. In spite of our efforts to make tight, high performing and quick-downloading code… we have to double or triple up on our CSS properties, which 90-95% of the time is unnecessary because they all work the same. And it’s a major pita. Boo!

  4. [...] BetterVideo HTML5 player which primarily targets the iPad which i first mentioned in a previous post. The player won’t be using the browser’s default controls — that would be [...]

  5. Huong Tinh Vu says:

    Hi Mike,

    Thank for your post. I’m new to CSS3, your post help me alot.

    About your note “NOTE: Those ratios are confusing. Using two fours would make them both 50%. You pretty much need to play around until it gives you the sizes you are looking for.” I think I know the rule that CSS3 use to calculate the remaining space.

    If you use #s1 {-webkit-box-flex:1;} and #s2 {-webkit-box-flex:4;} it mean #s1 will get 20% (not 25%) and #s2 will get 80% remaining space. So their “width” will be calculated by: “width” + percentage of remaining space they got.

    The percentage of remaining space for each will be calculate by: total remaining space / total of box-flex’s values * box-flex’s value (of each)

    Sorry for my English.

  6. Mike Wilcox says:

    Thanks Huong, I hope that helps somebody. I admit I struggled trying to convey the portions.