Adaptive design using flexbox (CSS)




What is it all about?

One of the ways to get an adaptive design on the website is called flexbox. It is a feature that came with CSS3 and can be highly useful. Flexbox is simply a way of splitting your elements up in boxes, that floats around on the user interface, depending on the width of the viewport (in practice, for most users, this is the same as the width of the screen). This means that you can have a design where the page has e.g. three columns with a specific width, if the viewport is wide enough, but only two or one, should the viewporten be too narrow to show the content in three columns.

Flexbox is used for sections of pages that needs to be flexible. If you need a global design with header, footer and sidebar, you use grid instead (I'll get to that).

The flexbox becomes especially interesting and useful, when it comes to controlling the flow of elements, and how the elements should align in the flexbox. It will make more sense when we get to the examples below.


Container and items

The flexbox consists of a container and items. Items are the elements floating around, and the container is the box, in which the items float. In other words, it looks like this:

The container



The container needs to be a tag that can contain other elements, so there are some limitations to what can be used here. In practice, it will most likely be a DIV that works, most of the time. Items can be almost anything. The item being a container itself, with its own items, it not a problem.

The way to specify that the tag is a flexbox is display:flex in STYLE. If you need the container to be an inline object, you use display:inline-flex instead. Here we use a class called FlexContainer:

.FlexContainer {
display:flex;
}



Sequence and flow direction for items

The first thing to specify, when you have your container and items, is whether the items should be aligned horizontally, in a row, or vertically, stacked in a column. This is done, using the variable flex-direction. Here you have the following options:

VariableEffect
rowArranged in a horizontal line, going from left to right (default)
row-reverseArranged in a horizontal line, going from right to left
columnArranged in a column, going from top to bottom
column-reverseArranged in a column, going from bottom to top


flex-direction: row


flex-direction: row-reverse


flex-direction: column




flex-direction: column-reverse




Right now the structure is fixed in one line or column, i.e. if it is wider than the width of the viewporten, you get a horizontal scrollbar (unless you have specified that it should't appear) and if it is smaller, you have some empty space. This can be made flexible, so that items that there is no room for in one line is moved to the next line, or if you have room for more columns, the items are are arranged as many columns as the width allows. This is the parameter flex-wrap.

If you find it hard to visualize, try changing width (and height) on the container below, by pulling the lower right corner. This is the effect of flex-wrap applied to a row.



For the parameter flex-wrap you have the following options:

VariableEffect
nowrapArranged in one line/column (default)
wrapArranged in multiple lines/columnds, depending on the available space. Horizontal line: from left to right. Column: from top to bottom.
wrap-reverseArranged in multiple lines/columnds, depending on the available space. Horizontal line: from right to left. Column: from bottom to top.


There is a parameter called flex-flow, which is a combination of flex-direction and flex-wrap, i.e. you can use the variables from these parameter, separated by space. Default is "flex-flow: row nowrap". If you are not used to work with flexbox, it is, like any other combination parameter, something I recommend you don't using. The code takes up less lines, but the trade off is readability of the code if you are untrained.

When you got the arrangement of the items in place, using rows or columns, you can start working with the alignment.


Horizontal alignment of items

Starting with the sideways alignment, this is specifies using the parameter justify-content, which is specified for the container. For this you have the following options:

justify-content: flex-start
Alignment with the left side of the container.

justify-content: flex-end
Alignment with the right side of the container.

justify-content: center
Alignment with the center of the container, without changing the distance between the items.

justify-content: space-between
Distribution of items in the entire width of the container, using same distance between all items.

justify-content: space-around
Distribution of items in the entire width of the container, using same distance between all items and the container's left and right side.



Vertical alignment of items

Vertical alignment is specified using the parameteren align-items, which is specified for the container. For this, you have the following options:

align-items: flex-start
1

2


3

4


Alignment to the top of the container.

align-items: flex-end
1

2


3

4


Alignment to the bottom of the container.

align-items: center
1

2


3

4


Alignment to the center of the container, according the the height of the item.

align-items: stretch
1

2


3

4


The height if the item is stretched to the height of the container. Be aware that if you have specified the height of the item, stretch won't work, as the parameter height always supersedes adjustments using stretch.

align-items: baseline
1

2


3

4


The content of the item is aligned according the baseline of the first line. The group of items is aligned to the top of the container.



Alignment with multiple lines of items

When you have multiple lines of items in the container, e.g. whe the items are rearranged due to differences in the viewport sizes, you can also specify the alignment for the entire container. For this you have the following options:

align-content:flex-start
1
2
3
4
5
6
7
8
Alignment to the top of the container. Notice that if the items differ in height, the height of the items in the line is adjusted to match the highest item. The container has been made flexible (grab and pull the lower right corner), so you can see how sizes and placement changes as the container changes size.

align-content:flex-end
1
2
3
4
5
6
7
8
Alignment to the bottom of the container. Notice that if the items differ in height, the height of the items in the line is adjusted to match the highest item. The container has been made flexible (grab and pull the lower right corner), so you can see how sizes and placement changes as the container changes size.

align-content:center
1
2
3
4
5
6
7
8
Alignment to the center of the container. Notice that if the items differ in height, the height of the items in the line is adjusted to match the highest item. The container has been made flexible (grab and pull the lower right corner), so you can see how sizes and placement changes as the container changes size.

align-content:stretch
1
2
3
4
5
6
7
8
The height of the items is adjusted to match the inner height of the container. The container has been made flexible (grab and pull the lower right corner), so you can see how sizes and placement changes as the container changes size.

align-content:space-between
1
2
3
4
5
6
7
8
The distance between the lines is adjusted so the lines fill out the container. Notice that if the items differ in height, the height of the items in the line is adjusted to match the highest item. The container has been made flexible (grab and pull the lower right corner), so you can see how sizes and placement changes as the container changes size.

align-content:space-around
1
2
3
4
5
6
7
8
The distance between the lines and the distance between the lines and the top and bottom of the container are adjusted to be the same. Notice that if the items differ in height, the height of the items in the line is adjusted to match the highest item. The container has been made flexible (grab and pull the lower right corner), so you can see how sizes and placement changes as the container changes size.



If for one or more specific items you need a different alignment, than the one specified by align-content, you are supposedly also able to do that. In practice it doesn't work well, but let us take a look at what the variable can do. The effect is obtained using the variable align-self, which is applied to the style of the item you want different.

If we look at the example using align-content:flex-start from before, and for item 5 specify align-self:flex-end, it looks like this:

1
2
3
4
5
6
7
8

Item 5 should have been all the way down at the bottom of the container, but instead it regains its original height, instead being stretched to match item 4, and the alignment is the bottom of the line where the item is situated.


Numbering of items

The default sequence for showing the items in the container, is the sequence in which they are written in the code, but this does not have to be the display sequence. For the item's style you have the parameter order, which is used for setting the sequence of items in the container. Order is an integer, the default value is 0, multiple items can have the same number, and negative values are allowed.

If we look at these items:

1
2
3
4
5
6

Now we can try giving each an order number (shown in brackets after the item number), and the sequence now looks like this:

1(2)
2(1)
3(-3)
4(5)
5(20)
6(1-)



Flexible width for items

If you want to give your items flexible width, this can be done using the variable flex-grow. Default value is 0, at which the item keeps its specified width, but specifying other values you can control the relative width. Here you should be aware that width is influenced by a the original size of the item, so items that were supposed have had the same size, may have different sizes.

If we look at three items, with two different sizes:

1
2
3

Then we can set flex-grow to 1 for all three items, whereby all three items combined are set to fill out the width of the container and have the same width. The result is this:

1
2
3

If you look closely, item 2 is a bit wider than 1 and 3, but they are almost the same size. It appears as if the space between the items is evenly distributed on the items, instead of being distributed to give the items the same size.

If one item is so wide that it would have to be smaller for all three items to have the same width, this item retains it's size and the remaining space for widening is distributed on the remaining items. Here item 2 has been made too wide. Then it looks like this:

1
2
3

By specifying different values for the items, you can control the relative sizes for the items. Here item 2 has been set to flex-grow:2, while the other two retains their flex-grow:1:

1
2
3

The claim is that flex-grow:2 should result in items twice as wide as items having flex-grow:1, provided there is enough space. For the same reasons as the three items didn't end up the same size in the example above, the relative sized don't end up in a 1:2 ratio in practice, as the should. Instead it supports the notion that the space before stretching is distributed in a 1:2 ratio on the items.

You cannot use negative values for flex-grow. If you need to make items narrower, you are supposed to use flex-shrink in stead. In practice you don't, as the parameter don't work, but for demonstration purpose:

These are the three items, where item 2 is too wide to form three equally wide items using flex-grow:

1
2
3

Now, if we change flex-grow:1 on item 2 to flex-shrink:1, it looks like this:

1
2
3

Item 2 has become narrower, as it was supposed to. Unfortunately the width of item 2 does not change, no matter what value you specify for flex-shrink, so you cannot in any way control the width.


Yet another parameter that is supposed to work, but doesn't work well in practice, is flex-basis. With this parameter, you can specify a default size for an item, from which the sizes of the other items are calculated. The absolute sizes (px, cm, etc.) works fine, but here you might as well use width. The relative sizes (percents) and auto don't work properly, and there is no advantage in using this parameter, compared to the use of width.


In stead of using flex-grow, flex-shrink and flex-basis, you can use a collective variable called flex. The syntax is "flex: flex-grow flex-shrink flex-basis" where the default is "flex: 0 1 auto". As flex-shrink and flex-basis work as poorly as they do, the general recommendation from here is to refrain from using this variable.