Taming Flexbox

Flexbox is very powerful, but consequently very abstract.

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.