Flexbox
This document is a work in progress.
- Author
- Christopher H. Harvey
- Description
- Using Flexbox to lay out Elements on a page.
- Keywords
- Flexbox
- layout
- CSS
- Less
- Version History
- Latest
Laying out a page is easier than it may seem. Everything on the page belongs in blocks, and each block has a size and position. That’s all there is to it. There are many methods, good and bad, of how to achieve this, but that is the gist of what needs to happen.
Of all the grid systems out there, this article does not discuss any of them. Rather, after reading this article, hopefully you will see the anti-need for any grid system, and certainly how ridiculous it is that there are so many of them. You will create your own layout (read, layout, not grid) using pure CSS3.
Before delving too far, though, you should probably get yourself up-to-speed on the Less CSS preprocessor, and the CSS Flexible Box Layout Module, or “Flexbox,” for short. This article is not a Flexbox tutorial, but rather a guide on how to use it to obtain the layout you want.
After you’ve designed the layout on paper (or in your head), you have to produce that layout on the screen. The easiest way to do this is to first decide on the order of the blocks, next set their widths such that they add up to the full page (or container) width, and then place the blocks into position. Flexbox makes this extremely easy: you can do this all in CSS, without ever having to touch your HTML.
Wait, what? You’re saying that presentation is not at all related to content? Let me reiterate. You can change the ordering, sizing, and positioning of elements on a page without having to touch the HTML. This means no wrapper divs, no table rows, no presentational classes, and no huge commits. Have I caught your attention yet?
Setting up Flexbox
The most important part of a site is its content, that is, the HTML. I cannot stress enough the importance of writing good, structured HTML first, and then styling it with CSS later. A lot of frameworks (ahem, Bootstrap) actually use HTML to affect style, which makes the source code unreadable and unmaintainable (think div soup). No, first we’ll write our content and structure, use good, meaningful classnames, and then use those classnames as style hooks in the CSS.
Let’s start with a prototypical example: a site with a header, footer, body copy, and two sidebars. We want our end result to show the header at top, footer at bottom, sidebars on left and right, and body copy in the middle. Something like this:
Not worring about how it looks for a moment, we need to write the HTML that makes the most semantic sense (and is the most accessible). This means our body copy should come first, so that it gets loaded first, followed by the header and footer, and then followed by the sidebars. Indeed, the order of our Elements does not match the order of our intended result, but CSS will take care of that later.
It might also be a good idea to add semantic classnames to the Elements so we can use them as stylehooks. This is not redundant though, because we don’t want to use Element selectors as style hooks for reasons not discussed in this article.
Now for the fun part: adding Flexbox.
To set up a layout with multiple blocks that will visually appear on one or more lines, we need to use the Flexbox model. In this model, a flex-container is a block that contains other blocks, called flex-items, inside.
Well, it’s a good thing our HTML is already structured this way! We already have a
container with 5 items. To turn the main.Article
into a flex-container,
use the GridFlex Object by adding class .o-GridFlex
to it.
(There is no need to add any class to its children.)
You should do this with <main class="Article o-GridFlex">
, but you
may use the Object as a mixin in your Less.
First notice how the items are now aligned in rows, and that if there are too many of them
or if they are too big, they wrap to the next row. This is a major advantage over grid systems
with <tr>
-like containers, which are very limiting.
Next notice how the width of each flex-item fits its contents. In this particular example,
the body copy is large compared to the other 4 items, but this would not be the case if
those items had more content. In the next sections, we’ll see how to adjust
the order and width of flex-items to make them go into the correct rows.
Setting Item Order
Use the .flex-order()
tool on an item to change its placement
in the order. The rules for ordering are simple:
- The flex-container will arrange its flex-items in increasing order.
- If two flex-items have equal ordinal numbers, the ordering will be the same as in the source (HTML).
- Every flex-item starts out with a default ordinal of 0. Thus the default ordering is that of the source.
- A flex-item’s ordinal can be changed to any integer, even a negative integer.
Before I spoon-feed you the answer, let’s take a moment to figure out what the ordinals should be. Here is a comparison of the items in order of source versus the order we want:
source | Body | Head | Foot | Side1 | Side2 |
---|---|---|---|---|---|
desired | Head | Side1 | Body | Side2 | Foot |
Body and Side2 are already in place, so we just need to rearrange the other items around them. This means keep the ordinal of Body and Side2 as the default 0, give the Head and Side1 an ordinal of -1 (and since they’re already in the correct order, they can have the same ordinal), and move the Foot to the end by giving it an ordinal of 1. Thus we need to make the following adjustments to our Less:
Remember, the actual ordinal numbers aren’t important. What’s important is their comparison to each other. We could have just as easily given the following ordinals to achieve the same effect.
.Body {.flex-order(1);} .Head {} .Foot {.flex-order(2);} .Side1 {} .Side2 {.flex-order(1);}
Likewise,
.Body {.flex-order(2);} .Head {.flex-order(0);} .Foot {.flex-order(4);} .Side1 {.flex-order(1);} .Side2 {.flex-order(3);}
Or even,
.Body {.flex-order(5);} .Head {.flex-order(2);} .Foot {.flex-order(11);} .Side1 {.flex-order(3);} .Side2 {.flex-order(7);}
Now, the items are in the correct order. Adjusting their widths will automatically put them into position, because Flexbox wraps!
Setting Widths
All we have to do is set the widths of each item. This will automatically place them into position
because of the way Flexboxes wrap. The easiest way to set the width of an Element is to use the CSS
width
property. You can do this with inline styles (yuck) or you can add it to
the Element’s class’ definition in an external stylesheet. Your life will be
significantly easier if you let Less do all the mathematical calculations, so you can
use math calculations (e.g., (5/6) * 100%
) or you can simply type in the
percentages yourself.
If we want our article to look like the screenshot in Figure 1, the Head and Foot should be full width, the Body should be a majority of the full width, and the sidebars should be the remainder, equal in length. For the middle part, 20% + 60% + 20% seems about right, but you can choose your own widths as long as they add up to 100%.
That’s it! Tutorial done. The rest of this document discusses further applications.
Helper Classes
Sometimes, setting the width in CSS is too much work, so
to quickly change the style of an Element using HTML, you may add
helper classes
to the [class
] attribute of the Element. Yes, this is bad practice,
but it’s a pragmatic solution that prevents CSS bloat. You can use these
so-called inline helpers for debugging/testing purposes, but in the long run, you’ll
want to migrate them over to an external stylesheet and use more semantic classnames.
That’s what Less includes and extends are for.
A little math will make it obvious which of these widths will combine to make
a full 100% (see the grid demo for a full
list of combinations).
// golden widths: @golden0 : 100%; @golden1 : 61.803%; @golden2 : 38.197%; @golden3 : 23.607%; @golden4 : 14.590%; @golden3c: 76.393%; @golden4c: 85.410%; .a-w-p0 { width: @golden0 !important; } .a-w-p1 { width: @golden1 !important; } .a-w-p2 { width: @golden2 !important; } .a-w-p3 { width: @golden3 !important; } .a-w-p4 { width: @golden4 !important; } .a-w-p3c { width: @golden3c !important; } .a-w-p4c { width: @golden4c !important; }
Warning:
These helpers use the keyword !important
,
so adding one to an Element’s [class
] attribute and then trying to override
it later with inline styles will result in a failure.
Positioning Items
When a line (that is, a visual horozontal arrangement of blocks) has only one block, you have a few options for how to position it.
The worst is using floats. This only pushes the block flush left or flush right, and wraps subsequent content around it, requiring a hack known as a clearfix to prevent this behavior. Floats are good for flyers and newsletters when you need to wrap text around an image, but they are not good at all for laying out a page.
Instead, you can use position: relative;
or position: absolute;
,
with a value for left
or right
. The downside of these is that you
cannot center a block on a line.
My recommendation is to use margins. To horizontally position a block, add left and right margins in the CSS.1
.-mx-a { margin-left: auto; margin-right: auto; }
Multiple Blocks
Most likely, your HTML is already structured this way: the blocks you want to
lay out together are likely already wrapped in some sort of container. All you need to do is add
the .o-GridFlex
Object to the container. Then, optionally use the width Utilities mentioned
above to set the proper widths of each of the items (if not, their widths will automatically
fit their content). That’s it! Layout done.
Luckily, the widths add up to 100%. If they add up to too much, the items will wrap to the next line. If they don’t add up to enough, there will be extra space.
By default, the extra space is evenly distributed between the items, much like
the inter-word space in a paragraph with text-align: justify;
.
To override this and distribute the space around the blocks, leaving a little bit
on the outside, use
the .-xj-sa
Utility in addition to
the .o-GridFlex
Object.
To completely remove the space between the items and push it all the way to the outside,
add the .-xj-c
Utility. This acts like a paragraph with
text-align: center;
.