Taming Flexbox
Flexbox is a recent addition to CSS for doing declarative page layout. You can use flexbox where you might have otherwise resorted to tables, floats, or other exotic hacks. For example, flexbox supports vertical centering, which curiously used to require a bizarre sequence of incantations, offerings to the soul of Aldus Manutius, and the patience of a terrazzo artisan. Or a willingness to use tables.
As powerful as flexbox is,
however, it’s not the most intuitive thing in CSS,
which is saying something.
We’re going to look at flexbox through the lens
of a Stylus helper function named flow
.
Our flow
function encapsulates common flexbox scenarios,
mapping the abstract flexbox terminology
to something a bit more concrete and intuitive.
Difficult To Reason About
Flexbox is confusing because it’s trying to do something difficult. It’s trying to transpose page layout constructs, like justification and alignment, into more abstract, direction-neutral variants. Instead of left and right or top and bottom, flexbox uses start and end. Instead of an X-axis and Y-axis, flexbox has a main axis and a cross-axis. While this is powerful, it’s also confusing and difficult to reason about.
Use Your Words
Our solution is to always use directions, like left, right, top, and bottom. We can also dispense with the distinction between justify and align, since we know that any given directional word can only refer to one thing, given the orientation of a given flow. We generate the corresponding flexbox code using our wrapper function. We’re using Stylus here, but you can do the same thing with any CSS framework.
Examples
For example, suppose we want a left-to-right flow, with the items left-justified and vertically aligned along their center. With our wrapper function, we’d write it like this.
flow row left center
This will generate the following flexbox declarations.
display flex
flex-direction row
justify-content start
align-items center
Suppose we flip the orientation and use a top-to-bottom (column) flow, again with the items center aligned. We’d write this instead.
flow column top center
This will generate the following, nearly identical, flexbox code.
display flex
flex-direction column
justify-content start
align-items center
We can guess from the direction of the flow that top refers to the justification.
The Magic
Our wrapper function is simpler than you might think. You can find the code on Github. It’s part of our Verse CSS framework.
The flow
function works similar to
CSS shorthand functions, like padding
or border
.
We simply pass in the values we want to set,
and the function figures out what they mean based on the context.
Stylus flow Function
vertical = top, bottom
horizontal = left, right, justify
-intersects(S, T)
for s in S
if s in T
return true
flow()
// okay, we know this much...
display flex
// handle wrap or no-wrap -- straightforward
if wrap in arguments
flex-wrap wrap
else if no-wrap in arguments
flex-wrap nowrap
// okay, column orientation
if column in arguments
// we know this
flex-direction column
// set alignment properties
$x = align-items
$y = justify-content
// otherwise, assume row
else
// we know this
flex-direction row
// set alignment properties
$x = justify-content
$y = align-items
// interpret left and right accordingly
if left in arguments
{$x} flex-start
else if right in arguments
{$x} flex-end
else if justify in arguments
// for now, this is only meaningful for
// row-based layouts
if row in arguments
// for now, we fake space-around with padding
justify-content space-between
// interpret top and bottom accordingly
if top in arguments
{$y} flex-start
else if bottom in arguments
{$y} flex-end
if center in arguments
// we know one orientation is 'center'
// but which one?
// start by figuring out if we have horizontal
// or vertical orientations already that we
// can use disambiguate things
$h = -intersects(arguments, horizontal)
$v = -intersects(arguments, vertical)
// if we have an horizontal orientation,
// but not a vertical orientation, we interpret
// 'center' as having a vertical orientation
if $h && !$v
{$y} center
// if have a vertical orientation,
// but not a horizontal one, we interpret
// 'center' as having a horizontal orientation
else if $v && !$h
{$x} center
// otherwise we interpret 'center' to mean
// that you want everything centered
else
justify-content center
align-items center
The first thing we do is
set the display
property to flex
.
We then check for wrap
or no-wrap
arguments,
setting the flex-wrap
property accordingly.
Things get a little more interesting when we check for the flex direction.
We check for an orientation argument,
defaulting to row
.
We set the flex-direction
property,
and define which axis correspond to which property.
We process the alignment/justification arguments next, using our property variables to actually define the properties.
The center
value is a bit tricky.
Unlike right
or top
,
center
could refer to either axis.
So we have to infer the meaning from the context.
If we have a value for the X-axis, but not the Y-,
we interpret center
to refer to the Y-axis.
And vice-versa.
And otherwise, if we don’t have values for either axes,
we interpret that to mean you want to center both.
So this would center things along the Y-axis:
flow left center
and this would center them along the X-axis:
flow top center
and this would center along both axis:
flow center
Future Work
We still have work to do.
Justifications can include space-around
and space-between
(we currently only allow space-between
and rely on padding to fake space-around
).
Alignments can also include stretch
and baseline
.
And we have yet to touch on the flex
property of flexbox items,
among other things.
Nonetheless, we’ve taken the first steps in taming flexbox. In the process, we’ve learned about how we can specify justification and alignment using flexbox.
More About Flexbox
Solved By Flexbox inspired this post. CSS Tricks has a great flexbox guide. Lee Jordan has written a nice introduction to flexbox, which includes a detailed summary of the problems flexbox solves.