Flexbox

The CSS Podcast - 010: Flexbox

A design pattern that can be tricky in responsive design is a sidebar that sits inline with some content. Where there is viewport space, this pattern works great, but where space is condensed, that rigid layout can become problematic.

The Flexible Box Layout Model (flexbox) is a layout model designed for one-dimensional content. It excels at taking a bunch of items which have different sizes, and returning the best layout for those items.

This is the ideal layout model for this sidebar pattern. Flexbox not only helps lay the sidebar and content out inline, but where there's not enough space remaining, the sidebar will break onto a new line. Instead of setting rigid dimensions for the browser to follow, with flexbox, you can instead provide flexible boundaries to hint how the content could display.

What can you do with a flex layout?

Flex layouts have the following features, which you will be able to explore in this guide.

  • They can display as a row, or a column.
  • They respect the writing mode of the document.
  • They are single line by default, but can be asked to wrap onto multiple lines.
  • Items in the layout can be visually reordered, away from their order in the DOM.
  • Space can be distributed inside the items, so they become bigger and smaller according to the space available in their parent.
  • Space can be distributed around the items and flex lines in a wrapped layout, using the Box Alignment properties.
  • The items themselves can be aligned on the cross axis.

The main axis and the cross axis

The key to understanding flexbox is to understand the concept of a main axis and a cross axis. The main axis is the one set by your flex-direction property. If that is row your main axis is along the row, if it is column your main axis is along the column.

Three boxes next to each other with an arrow, pointing left to right. The arrow is labelled Main axis

Flex items move as a group on the main axis. Remember: we've got a bunch of things and we are trying to get the best layout for them as a group.

The cross axis runs in the other direction to the main axis, so if flex-direction is row the cross axis runs along the column.

Three boxes of different heights, next to each other with an arrow, pointing left to right. The arrow is labelled Main axis. There's another arrow pointing top to bottom. This one is labelled Cross axis

You can do two things on the cross axis. You can move the items individually or as a group, so they align against each other and the flex container. Also, if you have wrapped flex lines, you can treat those lines as a group to control how space is assigned to those lines. You will see how this all works in practice throughout this guide, for now just keep in mind that the main axis follows your flex-direction.

Creating a flex container

Let's see how flexbox behaves by taking a group of different sized items and using flexbox to lay them out.

<div class="container" id="container">
  <div>One</div>
  <div>Item two</div>
  <div>The item we will refer to as three</div>
</div>

To use flexbox you need to declare that you want to use a flex formatting context and not regular block and inline layout. Do this by changing the value of the display property to flex.

.container {
  display: flex;
}

As you learned in the layout guide this will give you a block-level box, with flex item children. The flex items immediately start exhibiting some flexbox behavior, using their initial values.

The initial values mean that:

  • Items display as a row.
  • They do not wrap.
  • They do not grow to fill the container.
  • They line up at the start of the container.

Controlling the direction of items

Even though you haven't added a flex-direction property yet, the items display as a row because the initial value of flex-direction is row. If you want a row then you don't need to add the property. To change the direction, add the property and one of the four values:

  • row: the items lay out as a row.
  • row-reverse: the items lay out as a row from the end of the flex container.
  • column: the items lay out as a column.
  • column-reverse : the items lay out as a column from the end of the flex container.

You can try out all of the values using our group of items in the demo below.

Reversing the flow of items and accessibility

You should be cautious when using any properties that reorder the visual display away from how things are ordered in the HTML document, as it can negatively impact accessibility. The row-reverse and column-reverse values are a good example of this. The reordering only happens for the visual order, not the logical order. This is important to understand as the logical order is the order that a screen reader will read out the content, and anyone navigating using the keyboard will follow.

You can see in the following video how in a reversed row layout, tabbing between links becomes disconnected as the keyboard navigation follows the DOM not the visual display.

Anything which can change the order of items in flexbox or grid can cause this problem. Therefore any reordering should include thorough testing to check that it will not make your site hard to use for some people.

For more information see:

Writing modes and direction

Flex items lay out as a row by default. A row runs in the direction that sentences flow in your writing mode and script direction. This means that if you are working in Arabic, which has a right-to-left (rtl) script direction, the items will line up on the right. Tab order would also begin on the right as this is the way sentences are read in Arabic.

If you are working with a vertical writing mode, like some Japanese typefaces, then a row will run vertically, from top to bottom. Try changing the flex-direction in this demo which is using a vertical writing mode.

Therefore the way flex items behave by default is linked to the writing mode of the document. Most tutorials are written using English, or another horizontal, left to right writing mode. This would make it easy to assume that flex items line up on the left, and run horizontally.

With the main and cross axis plus the writing mode to consider, the fact that we talk about start and end rather than top, bottom, left, and right in flexbox might be easier to understand. Each axis has a start and an end. The start of the main axis is referred to as main-start. So our flex items initially line up from main-start. The end of that axis is main-end. The start of the cross axis is cross-start and the end cross-end.

A labelled diagram of the above terms

Wrapping flex items

The initial value of the flex-wrap property is nowrap. This means that if there is not enough space in the container the items will overflow.

A flex container with nine items inside it, the items have shrunk down so one word is on a line
but there is not enough room to show them side by side so the flex items have extended outside the
box of the container.
Once they hit min-content size flex items will start to overflow their container

Items displaying using the initial values will shrink as small as they can, down to the min-content size before overflow happens.

To cause the items to wrap add flex-wrap: wrap to the flex container.

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

When a flex container wraps it creates multiple flex lines. In terms of space distribution, each line acts like a new flex container. Therefore if you are wrapping rows, it is not possible to get something in row 2 to line up with something above it in row 1. This is what is meant by flexbox being one-dimensional. You can control alignment in one axis, a row or a column, not both together as we can do in grid.

The flex-flow shorthand

You can set the flex-direction and flex-wrap properties using the shorthand flex-flow. For example, to set flex-direction to column and allow items to wrap:

.container {
  display: flex;
  flex-flow: column wrap;
}

Controlling space inside flex items

Assuming our container has more space than is needed to display the items, the items line up at the start and do not grow to fill the space. They stop growing at their max-content size. This is because the initial value of the flex- properties is:

  • flex-grow: 0: items do not grow.
  • flex-shrink: 1: items can shrink smaller than their flex-basis.
  • flex-basis: auto: items have a base size of auto.

This can be represented by a keyword value of flex: initial. The flex shorthand property, or the longhands of flex-grow, flex-shrink and flex-basis are applied to the children of the flex container.

To cause the items to grow, while allowing large items to have more space than small ones use flex:auto. You can try this using the demo above. This sets the properties to:

  • flex-grow: 1: items can grow larger than their flex-basis.
  • flex-shrink: 1: items can shrink smaller than their flex-basis.
  • flex-basis: auto: items have a base size of auto.

Using flex: auto will mean that items end up different sizes, as the space that is shared between the items is shared out after each item is laid out as max-content size. So a large item will gain more space. To force all of the items to be a consistent size and ignore the size of the content change flex:auto to flex: 1 in the demo.

This unpacks to:

  • flex-grow: 1: items can grow larger than their flex-basis.
  • flex-shrink: 1: items can shrink smaller than their flex-basis.
  • flex-basis: 0: items have a base size of 0.

Using flex: 1 says that all items have zero size, therefore all of the space in the flex container is available to be distributed. As all items have a flex-grow factor of 1 they all grow equally and the space is shared equally.

Allowing items to grow at different rates

You don't have to give all items a flex-grow factor of 1. You could give your flex items different flex-grow factors. In the demo below the first item has flex: 1, the second flex: 2 and third flex: 3. As these items grow from 0 the available space in the flex container is shared into six. One part is given to the first item, two parts to the second, three parts to the third.

You can do the same thing from a flex-basis of auto, though you will need to specify the three values. The first value being flex-grow, the second flex-shrink, and the third flex-basis.

.item1 {
  flex: 1 1 auto;
}

.item2 {
  flex: 2 1 auto;
}

This is a less common use case as the reason to use a flex-basis of auto is to allow the browser to figure out space distribution. If you wanted to cause an item to grow a little more than the algorithm decides however it might be useful.

Reordering flex items

Items in your flex container can be reordered using the order property. This property allows the ordering of items in ordinal groups. Items are laid out in the direction dictated by flex-direction, lowest values first. If more than one item has the same value it will be displayed with the other items with that value.

The example below demonstrates this ordering.

Check your understanding

Test your knowledge of flexbox

The default flex-direction is

row
By default, flexbox will fit items into a row, lining them up at the start. With wrapping turned on, it will continue creating rows for children to flow within.
column
Setting flex-direction to column is a great way to stack elements, but it is not the default value.

By default, a flex container wraps children.

true
Wrapping must be enabled.
false
Use flex-wrap: wrap with display: flex to wrap children

A flex child item appears squished, which flex property helps mitigate this?

flex-grow
This property describes if elements can grow beyond a basis size, not how it should behave under a basis.
flex-shrink
Yes, this property describes how to handle sizing if the width is going below the basis.
flex-basis
This provides the starting point of sizing, but not how to handle sizing scenarios where width goes below basis, like in a squished scenario.

Flexbox alignment overview

Flexbox brought with it a set of properties for aligning items and distributing space between items. These properties were so useful they have since been moved into their own specification, you'll encounter them in Grid Layout too. Here you can find out how they work when you are using flexbox.

The set of properties can be placed into two groups. Properties for space distribution, and properties for alignment. The properties which distribute space are:

  • justify-content: space distribution on the main axis.
  • align-content: space distribution on the cross axis.
  • place-content: a shorthand for setting both of the above properties.

The properties used for alignment in flexbox:

  • align-self: aligns a single item on the cross axis.
  • align-items: aligns all of the items as a group on the cross axis.

If you are working on the main axis then the properties begin with justify-. On the cross axis they begin with align-.

Distributing space on the main axis

With the HTML used earlier, the flex items laid out as a row, there is space on the main axis. The items are not big enough to completely fill the flex container. The items line up at the start of the flex container because the initial value of justify-content is flex-start. The items line up at the start and any extra space is at the end.

Add the justify-content property to the flex container, give it a value of flex-end, and the items line up at the end of the container and the spare space is placed at the start.

.container {
  display: flex;
  justify-content: flex-end;
}

You can also distribute the space between the items with justify-content: space-between.

Try some of the values in the demo, and see MDN for the full set of possible values.

With flex-direction: column

If you have changed your flex-direction to column then justify-content will work on the column. To have spare space in your container when working as a column you need to give your container a height or block-size. Otherwise you won't have spare space to distribute.

Try the different values, this time with a flexbox column layout.

Distributing space between flex lines

With a wrapped flex container you might have space to distribute on the cross axis. In this case you can use the align-content property with the same values as justify-content. Unlike justify-content which aligns items to flex-start by default, the initial value of align-content is stretch. Add the property align-content to the flex container to change that default behavior.

.container {
  align-content: center;
}

Try this out in the demo. The example has wrapped lines of flex items, and the container has a block-size in order that we have some spare space.

The place-content shorthand

To set both justify-content and align-content you can use place-content with one or two values. A single value will be used for both axes, if you specify both the first is used for align-content and the second for justify-content.

.container {
  place-content: space-between;
  /* sets both to space-between */
}

.container {
  place-content: center flex-end;
  /* wrapped lines on the cross axis are centered,
  on the main axis items are aligned to the end of the flex container */
}

Aligning items on the cross-axis

On the cross axis you can also align your items within the flex line using align-items and align-self. The space available for this alignment will depend on the height of the flex container, or flex line in the case of a wrapped set of items.

The initial value of align-self is stretch, which is why flex items in a row stretch to the height of the tallest item by default. To change this, add the align-self property to any of your flex items.

.container {
  display: flex;
}

.item1 {
  align-self: flex-start;
}

Use any of the following values to align the item:

  • flex-start
  • flex-end
  • center
  • stretch
  • baseline

See the full list of values on MDN.

The next demo has a single line of flex items with flex-direction: row. The last item defines the height of the flex container. The first item has the align-self property with a value of flex-start. Try changing the value on that property to see how it moves within its space on the cross axis.

The align-self property is applied to individual items. The align-items property can be applied to the flex container to set all of the individual align-self properties as a group.

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

In this next demo try changing the value of align-items to align all of the items on the cross axis as a group.

Why is there no justify-self in flexbox?

Flex items act as a group on the main axis. So there is no concept of splitting an individual item out of that group.

In grid layout the justify-self and justify-items properties work on the inline axis to do alignment of items on that axis within their grid area. Due to the way that flex layouts treat items as a group, these properties are not implemented in a flex context.

It is worth knowing that flexbox does work very nicely with auto margins. If you come across a need to split off one item from a group, or separate the group into two groups you can apply a margin to do this. In the example below the last item has a left margin of auto. The auto margin absorbs all space in the direction it is applied. This means that it pushes the item over to the right, thus splitting the groups.

How to center an item vertically and horizontally

The alignment properties can be used to center an item inside another box. The justify-content property aligns the item on the main axis, which is row. The align-items property on the cross axis.

.container {
  width: 400px;
  height: 300px;
  display: flex;
  justify-content: center;
  align-items: center;
}

Check your understanding

Test your knowledge of flexbox

.container {
  display: flex;
  direction: ltr;
}

To vertically align with flexbox, use

align keywords
Nice
justify keywords
Sorry
.container {
  display: flex;
  direction: ltr;
}

To horizontally align with flexbox, use

align keywords
Sorry
justify keywords
Nice
.container {
  display: flex;
  direction: ltr;
}

By default, flex items are aligned to stretch. If you want content size used for child items, which of the following styles would you use?

justify-content: flex-start
The justify property is for horizontal alignment, not vertical.
align-content: start
content aligns flex lines, not child item alignment.
height: auto
This will have no effect.
align-items: flex-start
Yes, we want to vertically align them to the 'top' or start, which removes the default stretch value and instead uses the content height.

Resources