Oli.jp

Articles…

CSS3 Flexbox vs A Princess Bride

Caution: This article is about the old Flexbox spec (display: box;) — this is not the code you want to be copying. The new CSS Flexible Box Layout Module (display: flex;)

The scene in A Princess Bride where Inigo Montoya fights the Man in Black:

  1. Inigo Montoya Who are you?
  2. The Man in Black No one of consequence.
  3. Inigo Montoya I must know…
  4. The Man in Black Get used to disappointment.
The Princess Bride (1987)

I’ve always thought that line “Get used to disappointment” sums up our profession perfectly, and even more so Inigo’s shrug before continuing with the sword fight.

So… the Flexible Layout Box module has been around for ages, but unlike most CSS3 specifications it’s actually had browser support in Firefox and WebKit (Chrome, Safari) browsers for ages too. It allows us to lay out blocks of content, including things like vertical centering and equal height columns. Layout has to be the most painful area of CSS these days, so hey this is all sounding pretty damn good!

If you’re unfamiliar with Flexbox I recommend checking out Paul Irish’s Quick Hits with the Flexible Box Model for a quick overview. Do that now — I’ll wait…

He set out to seek his fortune across the seas

All sorted? Ok, what I’ve got for you is a little different — the straight dope from digging into this module for an upcoming Apress book on HTML5 and CSS3. As part of this I tried converting Nicole Sullivan’s OOCSS grid module into flexbox. It turned out to be disturbingly easy. Here is Nicole’s CSS — concise object-oriented CSS to create flexible grids in current browsers:

@media screen and (max-width: 319px) {.unit{float: none !important; width: auto !important;}}
.line:after,.lastUnit:after{clear:both;display:block;visibility:hidden;overflow:hidden;height:0 !important;line-height:0;font-size:xx-large;content:" x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x ";}
.line{*zoom:1;}
.unit{float:left;}
.size1of1{float:none;}
.size1of2{width:50%;}
.size1of3{width:33.33333%;}
.size2of3{width:66.66666%;}
.size1of4{width:25%;}
.size3of4{width:75%;}
.size1of5{width:20%;}
.size2of5{width:40%;}
.size3of5{width:60%;}
.size4of5{width:80%;}
.lastUnit{display:table-cell;float:none;width:auto;*display:block;*zoom:1;_position:relative;_left:-3px;_margin-right:-3px;}

And here’s the flexbox version

.unit,.line{display:box;}
.unit{box-orient:block-axis;box-flex:1;}
@media screen and (max-width:319px){.line{box-orient:vertical;}}

At least, that would be what we’d use in a supporting browser once the module is finalised. While Mozilla and WebKit support flexbox, as it’s still a work in progress we need vendor prefixes:

.unit {
	-moz-box-orient: block-axis;
	-moz-box-flex: 1;
	-webkit-box-orient: block-axis;
	-webkit-box-flex: 1;
	box-orient: block-axis;
	box-flex: 1;
}
.unit, .line {
	display: -moz-box;
	display: -webkit-box;
	display: box;
}
@media screen and (max-width: 319px) {
	.line {
	-moz-box-orient: vertical;
	-webkit-box-orient: vertical;
	box-orient: vertical;
	}
}

It’s probably a local fisherman out for a pleasure cruise at night, through eel-infested waters

Now before we see how that goes, here’s what I learned along the way:

  1. Firefox treats display:box as display:inline-boxmake sure you declare width on the flexbox element if you use display:box
  2. The preferred width of a box element child containing text content is currently the text without line breaks, leading to very unintuitive width and flex calculations → declare a width on a box element child with more than a few words (ever wonder why flexbox demos are all “1,2,3”?)
  3. Obviously you would prefer to make a liquid layout rather than use px, but Firefox ignores child widths in percentages (ever wonder why whole page flexbox demos are all px?). Oh crap. The workaround is to use width: 0 and let box-flex dictate things.
  4. WebKit allows content too wide for the box element’s child to overflow out of the container box, but Firefox ignores width and expands the child. Firefox also doesn’t respect overflow:hidden (or auto or scroll) on box element children with content larger than the child’s declared width, such as <pre> content or a very long word → make sure your content won’t overflow
  5. There appears to be a bug in WebKit’s shrinking algorithm if child widths are not specified, or if child widths are significantly larger than the box element width, if the box-flex values differ significantly. This can lead to the child with the largest box-flex value becoming zero width (ignoring their intrinsic minimum width) → make sure your flexbox element is big enough for its content
  6. Using display:box prevents margin collapsing for the box element’s children in WebKit and Firefox. That’s cool, however Firefox also doesn’t collapse margins between the box element and surrounding content.
  7. Firefox pads outline on flexbox children by the same amount as the outline’s width

He didn’t fall!? Inconceivable!

I saved the best for last. When flexbox declarations are nested they are calculated from the inside out, rather than top down. Combined with our inability to use percentage widths for children in Firefox, and how the preferred width of text content is determined atm, this means Firefox’s flexbox implementation is unusable for liquid nested layouts atm — inner boxes take all the space, leaving little for outer boxes. Setting child widths to percentages works fine in WebKit, and using widths other than percentages works fine in Firefox.

I donna suppose you could speed things up?

Let’s see that in action — first the OOCSS version:

The AG test with OOCSS

1/5

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/3

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1

Lorem ipsum dolor sit amet...

1/5

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Now let’s try our flexbox version:

The AG test with flexbox

1/5

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/3

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1

Lorem ipsum dolor sit amet...

1/5

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Oh dear, you’ll notice the widths are somewhat … different.

Finally, one more time using OOCSS’s percentage widths for .unit, for your WebKit comparison pleasure.

The AG test with flexbox plus percentage widths

1/5

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/3

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1/2

Lorem ipsum dolor sit amet...

1

Lorem ipsum dolor sit amet...

1/5

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Lorem ipsum dolor sit amet...

Note: I’ve changed the @media rule in these demos to apply under max-width:700px, so the effect is visible here if you resize your browser.

There’s not a lot of money in revenge

Well, crap. Flexible Box Layouts are only good for non-liquid layouts in WebKit and Mozilla, or liquid layouts in WebKit only. That’s a pretty small slice of happy pie. But it’s not all bad. Despite Flexbox being by far the closest thing we have to a usable layout method in CSS, it’s actually pretty pants for page layout, as Tab Atkins explains in Why Flexboxes Aren’t Good for Page Layout. Still, as Wesley would say, if we only had a wheelbarrow, that would be something.

And even better, Tab and other members of the CSS Working Group are currently rewriting the Flexible Box Layout module, taking all of this into account. Admittedly, this means changing from box-* to flex-*, which means we’re back to no browser support and the bugs above may never be fixed. But looking on the bright side, along with the Template Layout module, the somewhat abandoned Grid Positioning module, or even the recently proposed Grid Alignment module CSS Regions Module Level 3 and the reborn CSS Grid Layout, one day, one day, we will have a layout module worthy of true love.

The important part is not getting used to disappointment. The important part is the shrugging and battling on.

If you have any feedback, please let me know via Twitter (@boblet).

Changes

  1. Noticed some typos, and added a warning and an updated list of future layout modules too