blogstrapping

Use Flexbox For CSS Layouts

WARNING: This is a work in progress. Some things have been written almost as stream of consciousness, and not yet tested. Use at your own risk.

This documents the use of some CSS flexbox layout properties applied to a container element, the "flexbox parent", and the child elements it contains, the "flexbox children":

Parent

Child

Basic Flexbox

This deals with how flexbox properties may be very simply used to arrange a primary navigation element.

HTML

Focusing on a single piece of an overall webpage layout should help keep this example simple and easy to understand. Primary site navigations will likely be the most common ways you will use flexbox, so a very common approach to laying out navigation will serve as our example here, with ul inside of a nav element as the flexbox parent and several li elements as the children of that flexbox parent.

<nav>
  <ul>
    <li><a href="#">First</a></li>
    <li><a href="#">Second</a></li>
    <li><a href="#">Third</a></li>
  </ul>
</nav>

CSS

Setting display to flex makes your flex container's children behave as block items, and inline-flex makes them behave as inline items. The default value for flex-direction is row, which arranges these block items horizontally, but it can be set to column to arrange them vertically. You can also set flex-direction to column-reverse or row-reverse to arrange them in the reverse of the order in which they are specified in your HTML in a column (vertical) or row (horizontal) arrangement, respectively.

Some browsers require prefixed names for some flex properties. An automatic prefixer tool like post-css can be used to handle the complexity of any cross-browser compatibility issues for you.

nav ul {
  list-style: none;
  display: flex;
  flex-direction: column-reverse;
}

Flexbox Arrangement

This deals with how the order property of flexbox children can be used to arrange them on a page. This is probably most useful for two purposes: changing the arrangement order dynamically within a page, and changing the arrangement order of ordered list elements.

HTML

We will use an example with a body element as a flexbox parent. That parent element will have four immediate children: header, nav, main, and footer. The ul element within nav will be another (nested) flexbox parent element, with several li children, just for the sake of a simple nesting example to illustrate a common layout pattern, though the focus of this section is on the use of the order property for the body flexbox parent's children.

<body>
  <header>Header</header>

  <nav>
    <ul>
      <li><a href="#">First</a></li>
      <li><a href="#">Second</a></li>
      <li><a href="#">Third</a></li>
    </ul>
  </nav>

  <main>Content</main>
  <footer>Footer</footer>
</body>

CSS

As indicated in the "Basic Flexbox" section above, using the default flex-direction property means arranging the children as a row, horizontally across the screen. In the case of content elements, this typically means creating a visually columnar appearance to content blocks on the screen.

The order property defaults to zero. When multiple children of the flexbox parent have the same order value, they simply appear in the order they are specified in your HTML. Order properties are set in the child elements, and when order values are compared between flexbox children the higher values are moved after the lower values. Negative values can be used, which results in zero (the default value) being treated as the center around which other values are arranged, with negative values determining how far before zero order elements the negative value element is placed, and positive values determining how far after.

body {
  display: flex;
  flex-direction: column;
}

header {
  order: -1;
}

nav {
  order: 1;
}

nav ul {
  list-style: none;
  display: flex;
}

footer {
  order: 1;
}

Giving header an order of negative one guarantees it comes before all other children of the body flexbox parent in this example, because all other elements have the default value of zero or a positive number value. Setting nav to one pushes it to the bottom because no other flexbox child element has a higher positive number value; setting footer to one as well also pushes it to the bottom, and because both of them have an order property value of one they are arranged relative to each other in the same order they are arranged in the HTML.

Flexbox Alignment

This deals with alignment of flexbox child elements, using the common pattern of multiple articles of different lengths in a single page, a layout characteristic common to blogs.

HTML

<body>
  <div>
    <h2>First Post!</h2>
    <p>This is an uninteresting article with two paragraph elements.</p>
    <p>The second paragraph element is more boring than the first.</p>
  </div>

  <div>
    <h2>Second Post</p>
    <p>This is a very short second article.</p>
  </div>

  <div id="longest">
    <h2>Third Post</h2>

    <p>
      By the third article, this writer found more to say about something.
      As a result, it ended up being a longer article than either of the
      preceding two, not only in paragraph count but in word count per
      paragraph.
    </p>

    <p>Of course, the per-paragraph word count is an average.</p>

    <p>
      The difference in article length ensures wildly different lengths on
      a small page so that the results of making alignment changes can be
      demonstrated very clearly in this example.  There is no other
      particular reason for writing this much text for an HTML example in
      this documentation.
    </p>
  </div>
</body>

CSS

The justify-content property in the flexbox parent element affects usage of whitespace between flexbox children uniformly, aligning the children along the flex-direction property's flow. Thus, if you set flex-direction to a value of column, you arrange children vertically, and justify-content aligns them vertically, but if you set flex-direction to row to arrange flexbox children horizontally, the justify-content property aligns children horizontally. Put another way, justify-content determines how to distribute space around and between elements vertically when using column and horizontally when using row for the flex-direction property.

The default is start, which groups all flexbox child elements at the top of the area within the flexbox parent. A value of end does the same at the end of the parent, and center will group them in the middle. These three values serve the most common alignment needs people have previously wanted to use other alignment properties, but this flexbox approach probably meets intuitive expectations better than those other CSS alignment properties -- especially for vertical alignment, as in this flexbox-direction: column example.

Other possible justify-content values include space-between, which places the first element at the beginning of the parent element with no excess space before it, and the last element at the end with no excess space after it, distributing any excess space equally between the child elements; and space-around, which divides excess space equally amongst the child elements, half of the excess space assigned to each element placed before it and the other half after it. This results, visually, in the space between flexbox child elements appearing to be twice the size of the space before the first element and the space after the last element.

The align-items property aligns flexbox children in a direction perpendicular to the flex-direction flow. Thus, for flex-direction: column, which arranges children vertically, align-items aligns children horizontally; for flex-direction: row, which arranges children horizontally, align-items aligns children vertically.

The default value of align-items is stretch, which ensures that with flex-direction: column the width of flexbox children is 100%, while with flex-direction: row the height of child elements is 100% instead. Changing the align-items property to flex-start, flex-end, or center will set the appropriate dimension (width or height) to the minimum for the element's content, then place the element flush with the beginning or end of the direction perpendicular to the flex-direction or in the center of the provided area, as appropriate. When using flex-direction: row, this conveniently mitigates the complexity of using other approaches to trying to vertically align block elements with CSS.

Another align-items value is baseline, which minimizes the space taken up by a flexbox child element like flex-start, flex-end, or center. In a flex-direction: row arrangement, the baseline value for align-items aligns the baseline of the first content line of all flexbox children, which means that, for instance, the bottom of the first line of text in each child could be aligned with the bottom of the first line of text in the others. This is probably most useful when you want to align single-row elements with different sizes at the top of all flexbox children, such as when showing multiple content blocks arranged horizontally with an image at the top of each block. [ NOTE: CHECK THIS TO MAKE SURE IT WORKS THIS WAY. ]

You can also use the align-self property to align flexbox children individually. Apply the property to a flexbox child to affect only the alignment of that specific child, rather than to the flexbox parent. The default value is auto, which just causes the child to do what you specified in the flexbox parent's align-items property.

body {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: flex-end;
}

#longest {
  align-self: stretch;
}

Flexbox Dimensions

This deals with the sizes of flexbox child elements, and how both positive and negative excess space gets used when the amount of available flex-direction space is different from the dimensions specified for child elements.

HTML

<nav>
  <ul>
    <li id="first"><a href="#">First</a></li>
    <li id="second"><a href="#">Second</a></li>
    <li id="third"><a href="#">Third</a></li>
  </ul>
</nav>

CSS

Normally, with flex-direction: row, the width of a flexbox child element shrinks to its content width. The same applies to the height of a flexbox child when using flex-direction: column. This can be overridden with flex-basis.

The flex-basis property for a flexbox child applies to the dimension of the element that matches the flex-direction. Thus, for flex-direction: row, it applies to width, and for flex-direction: column, it applies to height. In fact, the flex-basis property overrides the width property for row, and overrides the height property for column, but it overrides neither max-width or min-width for row, nor max-height or min-height for column. The flex-basis property defaults to auto, but can be set using standard CSS dimension units, such as em or px.

The flex-grow property, which defaults to flex-grow: 0, has a unitless numeric value used to set CSS policy for what to do with any excess space along the dimension that corresponds to the value of flex-direction, where a value of one is equivalent to one share of excess space, and a value of two is equivalent to two shares. Setting the flex-grow property for exactly one child element to 1 ensures that element will use all excess space, increasing its effective flex-basis value to its explicit value plus whatever extra space remains after allocating flex-basis space to all other flexbox children. Setting the first child element's flex-grow to a value of 2 and the second's to 1 means there are three equal shares of whatever excess space remains, assigning two shares of it to the first element and one share to the second.

To allocate the total space the same way, use flex-grow to show the share of total space applied to each element, and set flex-basis to zero. This means that each child element has a basis of zero space, which means all space on the line is "excess" space, and flex-grow then divides the total space into the shares defined for each element, thus giving the first element in the example a total space share of two and the second element a total space share of one.

The flex-shrink property sets policy for what happens when there is not enough total space available to meet the requirements of flex-basis values. The default value of flex-shrnk is one, which means all child elements lose the same amount of space. If you set flexbox-shrink: 2 for one element, that element will lose flex-basis value twice as quickly as others with flex-shrink: 1 when there is not enough space to meet the requirements of explicit flex-basis settings. If you set flex-shrink: 0 for an element, that element's space will not shrink at all, causing all shrinkage to apply to other flexbox children; setting flex-shrink: 0 for all flexbox children will cause the space used by the flexbox parent to overflow available space if necessary so that child elements do not get robbed of specified space. The effects of flex-shrink will not shrink an element's space so much that it cuts off the element's content, though.

The simple property name flex provides a shortcut for all three of these flex dimension properties. It takes up to three values, with default values flex-grow: 1, flex-shrink: 1, and flex-basis: 0, in that order. Notice that 1 is the default value for flex-grow in the flex property instead of 0 as normal for when not defined in flex-grow, and 0 is the default value for flex-basis by the flex property, instead of auto as normal for when do not define by the flex-basis property. This difference in defaults is likely to prove confusing and frustrating if you do not keep it in mind. Normally, setting one or two of the three possible values of flex sets either the first value (flex-grow), or the first and second values (flex-grow and flex-shrink), respectively, which then ensures the flex-basis value gets a default of 0 because the flex shorthand's default value for flex-basis overrides the explicit flex-basis default itself. Because flex-grow and flex-shrink only take unitless numeric values, flex: 250px will apply the 250px value to flex-basis, and flex: 2 250px will behave like you set flex-grow: 2 and flex-basis: 250px. In this way, the use of units on the last value can alter the normal application precedence of flex property values.

A good reason for these differing defaults is that setting flex: 1 for all flexbox children gives them all the equivalent of flex-grow: 1, flex-shrink: 1, and flex-basis: 0, which means all children grow and shrink as needed to maintain the same size along the flex-direction dimension, a very common use case. The downside of this is that defaults are different from how they work for the long-form properties, which can cause problems when trying to figure out why flexbox styles do not make the layout look the way you expect. The most reasonable approach for most purposes might simply be to use flex all the time, and never use the flex-grow, flex-shrink, and flex-basis properties themselves.

body {
  display: flex;
}

#first {
  flex-basis: 200px;
  flex-grow: 2;
}

#second {
  flex-basis: 100px;
  flex-grow: 1;
}

#third {
  flex-basis: 150px;
  flex-shrink: 2;
}

Flexbox Wrapping

This deals with breaking the space usage into multiple columns or rows, as appropriate for flex-direction, when flexbox child elements overflow the available space.

HTML

<body>
  <div class="thumbnail"><img src="/path/to/img/1.jpg" /></div>
  <div class="thumbnail"><img src="/path/to/img/2.jpg" /></div>
  <div class="thumbnail"><img src="/path/to/img/3.jpg" /></div>
</body>

CSS

The flex-wrap property allows a row to break into multiple lines within a flexbox, or columns to break into multiple rows.

If you set flex-wrap: wrap, rows of flexbox children break before running out of container room in the flexbox parent element as lines normally break for text, or columns break vertically in a similar way. A setting of flex-wrap: wrap-reverse reverses the arrangement of elements and wraps from the end to the beginning, instead of from the beginning to the end. Alignment properties can be used to modify the arrangement of elements, such as justify-content: space-around and align-content: space-between. Some alignment properties (align-items and align-self) apply only to the block-level elements arranged in rows or columns, and have no effect on content elements.

body {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: space-between;
}

.thumbnail {
  display: block;
  position: relative;
  margin: 0;
  flex: 0 100px;
  height: 100px;
}