Creating Flexible Layouts with Flexbox

Tiffany Brown
Share

The following introduction to Flexbox is an extract from Tiffany’s new book, CSS Master, 2nd Edition.

Before CSS Grid came along, there was Flexbox (which is officially known as the CSS Flexible Box Layout Module). Flexbox was designed to manage layout in one direction—a row (flex-direction: row or row-reverse) or a column (flex-direction: column or column-reverse). That’s in contrast to Grid, which accounts for rows and columns.

A basic flexible box layout is simple to create: add display: flex or display: inline-flex to the containing element. These values for display will trigger a flex formatting context for that containing element’s children. As with Grid, both flex and inline-flex are inside display modes. We set these values on the container, which behaves like a block-level or inline-level box, respectively. The children of that container are then arranged according to the rules of flex layout.

Note: Older versions of Blink-based browsers such as Chrome (≤ 28), and WebKit-based browsers like Safari (≤ 8), require a vendor prefix. If your project still supports those browsers, you’ll need to use display: -webkit-flex or display: -webkit-inline-flex. Older versions of Firefox (≤ 21) also require a prefix. Use -moz-flex and -moz-inline-flex to support those browsers.

By adding display: flex or display: inline-flex to a containing element, its immediate children become flex items, as shown in the image below. Flex items may be element children or non-empty text nodes. For instance, the markup below generates three flex item boxes that each behave according to the rules of flex layout:

<div style="display: flex">
    <span>This text is contained by a SPAN element.</span>
    <b>This text is contained by a B element.</b>
    This text node is still a flex item.
</div>

If no other properties are set, each flex item will have the same height as its tallest element (as determined by content). It will also stack horizontally (or vertically when the document has a vertical writing mode) without wrapping, and with no space between the edges of each box. Flex items may overflow the container.

A list with display: flex applied to the ul containing elementA list with display: flex applied to the ul containing element

This may not seem like such a big deal, but it simplifies the code necessary for a range of user interface patterns. Let’s look at a couple of examples.

A New Media Object Component

Take the following media object code:

<div class="media__object">
    <img src="video-thumb1.jpg">
    <div class="media__object__text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
</div>

Before Flexbox, we might have paired the preceding markup with the following CSS:

.media__object img {
    float: left;
    height: auto;
    width: 150px;
}
.media__object__text {
    padding-left: 170px;
}
/* Let's use the clearfix hack! */
.media__object::after{
    content: ' ';
    display: block;
    clear: both;
}

This layout works, but it has one major drawback: it requires us to constrain the width of our images so that we know how much padding to use. That limits our ability to use this same component in multiple contexts. You may want an image 150px wide when this component is used for a “Related Stories” widget, and one that’s only 75px wide for comments.

Let’s try this using Flexbox. Here’s our updated CSS:

.media__object {
    display: flex;
}
.media_object img {
    margin-right: 20px;
}

That’s a lot less CSS. An added bonus is that we don’t have to worry about how wide or tall our image is. Nor do we have to concern ourselves with clearing floats. Whether the image is 200px wide or 20px wide, .media__object__text will abut the margin box of our img element.

Creating Flexible Form Components with flex

Another use case for Flexbox is creating flexible, vertically aligned form components. Consider the interface pattern shown in the image below.

A form field with an adjacent buttonA form field with an adjacent button

Here, we have a form input control and an adjacent button. Both are vertically aligned, and our button is 150px wide.

What if we want our input element to expand to fill the available space in its container? Without Flexbox, we’d need some JavaScript and hand-waving to update the width of input in response to changes in the width of its parent. With Flexbox, however, we can just use flex.

The flex property is actually shorthand for three other properties.

  • flex-grow indicates that an element should grow if necessary and must be a positive integer. Its initial value is 0.
  • flex-shrink indicates that an element should shrink if necessary and must be a positive integer. Its initial value is 1.
  • flex-basis: indicates the initial or minimum width (when the flex axis is horizontal) or the height of an element (when it’s vertical). It may be a length or percentage, or auto, and its initial value is auto.

Though it’s possible to set each of these individually, the specification strongly recommends using the flex shorthand. Here’s an example:

div {
    display: flex;
}
input[type="text"], button {
    border: 0;
    font: inherit;
}
input[type="text"] {
    flex: 1 0 auto;   
}
button {
    background: #003;
    color: whitesmoke;
    display: block;
    text-align: center;
    flex: 0 0 150px;
}

Here, we’ve used flex: 1 0 auto for our input element. Since its flex-grow value is 1, it will grow to fill the available space of its parent. For the button element, however, we’ve used flex: 0 0 150px. The 0 values for flex-grow and flex-shrink prevent the width of the button from increasing or decreasing, while the flex-basis value of 150px sets its width.

As you can see in the image below, our button remains the same size, but the width of input expands to fill the remaining space.

The effect of flex: 0 0 150pxThe effect of flex: 0 0 150px

The tricky bit about flex-grow and flex-shrink values is that they’re proportional. Yes, flex: 1 0 auto means our input element will be wider than our button. But changing the value of our button’s flex property to flex: 1 0 auto doesn’t necessarily mean that both elements will have the same size, as shown in the following image.

Both items have the same flex value but are still different sizesBoth items have the same flex value but are still different sizes

Instead, flex items will be resized to fill the container, taking their used min-width and max-width values into account (which may be their initial values).

Unfortunately, we can’t use fr units with the flex or flex-basis properties. Use length or percentage values instead.

Vertical Centering with Flexbox

Finally, let’s take a look at how to vertically center content with Flexbox. Vertically centering elements is one of the more difficult tasks to achieve with CSS, particularly if the height of your content is unknown. But with Flexbox, we require just one additional line of CSS—align-items: center:

.flex-container {
    display: flex;
    align-items: center;
}

Now our flex items and their contents are vertically centered within the flex container, as shown in the image below.

Distributing flex items with align-items: centerDistributing flex items with align-items: center

Creating Grid-like Layouts with Flexbox

In most cases, you’ll want to use Grid to create grid-like layouts. However, you may find yourself wanting boxes that align when there’s an even number of items, but expand to fill the available space when there’s an odd number.

A grid-like layout with expanding cellsA grid-like layout with expanding cells

Here’s the markup we’ll use:

<ul class="flex-aligned">
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>D</li>
    <li>E</li>
    <li>F</li>
    <li>G</li>
</li>

By default, flex items don’t wrap. To achieve the layout above, we’ll need to make them wrap using the flex-wrap property. It accepts three values: nowrap (the inital value), wrap, and wrap-reverse. We’ll use wrap here:

.flex-aligned {
    display: flex;
    flex-wrap: wrap;
}

Now we just need to indicate how our flex items should behave, and what their maximum width should be. Since we want a maximum of four columns, we’ll set our flex-basis value to 25%. And since we want our flex items to expand to fill the available space, we’ll set flex-grow to 1. We’ll keep flex-shrink at 0 so that our boxes never occupy less than 25% of their container:

.flex-aligned li {
    flex: 1 0 25%;
}

Learning More about Flexbox

There’s a lot more to Flexbox than what we’ve covered here. CSS-Tricks’ “A Complete Guide to Flexbox” digs into all the properties and values. You can also check out Philip Walton’s “Solved by Flexbox”, which showcases UI patterns that are made easier with Flexbox.

Choosing flex or grid

As you develop page or component layouts, you may find yourself wondering when it’s better to use Flexbox and when to use Grid.

  • Use Grid when you want to arrange elements into rows and columns that align both horizontally and vertically.
  • Use Flexbox when you want to arrange items in a row or a column, when you wish to align items vertically or horizontally, but not both.

Jen Simmons’ video “Flexbox vs. CSS Grid — Which is Better?” walks you through some things to consider when choosing between Grid and Flexbox. Rachel Andrew’s “Should I use Grid or Flexbox?” is another great resource for understanding both.

In practice, your projects will probably mix both of these techniques, as well as floats. For instance, you may use Grid to define the overall page layout, while using Flexbox for your navigation menu or search box, and floats to place tables or images.