Computer scienceFrontendCSSFlexbox

Flexibility, growth and contraction ratio

14 minutes read

In real life, we often need to make a flexible item that scales to a certain width with its content. If we had to define the width of the flex item manually, flexbox would not be such a holy grail that it is. It's possible to manipulate the flex items' width and flexibility just by setting their flex-grow or flex-shrink properties.

Flex-basis

First of all, let's discuss flex-basis. This property defines the initial size of the item along the main axis.

Here's an example:

<div class="flex-container">
  <div class="flex-item item1">50px</div>
  <div class="flex-item item2">100px</div>
  <div class="flex-item item3">50px</div>
  <div class="flex-item item4">70px</div>
</div>
.flex-container {
  border: 1px solid black;
  display: flex;
  width: 400px;
}

.flex-item {
  border: 1px solid black;
  text-align: center;
  margin: 5px;
}

.item1 {
  flex-basis: 50px;
}

.item2 {
  flex-basis: 100px;
}

.item3 {
  flex-basis: 50px;
}

.item4 {
  flex-basis: 70px;
}

There are four items inside the container with widths given them by flex-basis. Note there is some space left inside the container because the sum of items' lengths is less than 400px even with items' margins, and no correcting properties have been applied.

Four rectangles different widths on one row

Flex-grow

What if we want items to occupy all available space inside the container? The flex-grow property defines how to increase the width of an item: in other words, it defines how much of the remaining free space the item takes.

If the size of the parent container is more than the width of all the items combined, the remaining space will be distributed among them in proportion to their flex-grow factor. For example, if for all the elements the property is flex-grow: 1, they will all have the same size because each will take the same part of the free space. However, if one of them has 2 as a flex-grow value, it will take twice as much free space as the others. The default value of flex-grow is 0.

As an example, let's take a container with four initially identical blocks:

<div class="flex-container">
  <div class="flex-item item1">item1</div>
  <div class="flex-item item2">item2</div>
  <div class="flex-item item3">item3</div>
  <div class="flex-item item4">item4</div>
</div>
.flex-container {
  border: 1px solid black;
  display: flex;
  width: 400px;
}

.flex-item {
  border: 1px solid black;
  text-align: center;
  margin: 5px;
  flex-basis: 50px;
}

The result is:

Four rectangles with the same width in one row

Then add flex-grow: 1 to all items:

.flex-container {
  border: 1px solid black;
  display: flex;
  width: 400px;
}

.flex-item {
  border: 1px solid black;
  text-align: center;
  margin: 5px;
  flex-basis: 50px;

  flex-grow: 1;
}

Now items share free space between themselves equally:

Four items streched in full width of a parent block

And, to see the difference, let's add another value of flex-grow to item2:

.flex-container {
  border: 1px solid black;
  display: flex;
  width: 400px;
}

.flex-item {
  border: 1px solid black;
  text-align: center;
  margin: 5px;
  flex-basis: 50px;
  flex-grow: 1;
}

.item2 {
  flex-grow: 2;
}

The final result is:

Four items have different width and strech for all parent block width

This time the free space is distributed among the items not equally. The second item is bigger than the others because its flex-grow value is more than that of the others.

The remaining space is divided by the sum of the values, which in this case is 1 + 2 + 1 + 1 = 5. The result of the division is the size of one part. So items 1, 3 and 4 will get one part because their flex-grow property value is 1, and the second item with flex-grow: 2 will get two parts.

Flex-shrink

Now consider the case when the width of all the items combined is more than the size of their parent. Here you can use flex-shrink, if it is possible to sacrifice some width of items. flex-shrink and flex-grow work in pretty similar ways dividing the value "width of the container minus the width of all the items" between items in some proportion. Since in the considered case the width of the container is smaller than the width of all the items, we get the negative value.

There is a special formula for calculating how much an element will shrink according to its flex-shrink value. Let's say the container has width: 400px, and the four elements have widths of 100, 200, 100, and 150 px respectively.

<div class="flex-container">
  <div class="flex-item item1">100px</div>
  <div class="flex-item item2">200px</div>
  <div class="flex-item item3">100px</div>
  <div class="flex-item item4">150px</div>
</div>
.flex-container {
  border: 1px solid black;
  display: flex;
  width: 400px;
}

.flex-item {
  border: 1px solid black;
  text-align: center;
  margin: 5px;
}

.item1 {
  flex-basis: 100px;
}

.item2 {
  flex-basis: 200px;
}

.item3 {
  flex-basis: 100px;
}

.item4 {
  flex-basis: 150px;
}

The result is:

Items are overflowed the parent block

Although the first three items combined are 400px, and the container too, item 3 is partially out of the container because some space inside it has already been taken by the items' margins.

Now let's also give items flex-shrink of 1, 2, 3, and 4:

.flex-container {
  border: 1px solid black;
  display: flex;
  width: 400px;
}

.flex-item {
  border: 1px solid black;
  text-align: center;
  margin: 5px;
}

.item1 {
  flex-shrink: 1;
  flex-basis: 100px;
}

.item2 {
  flex-shrink: 2;
  flex-basis: 200px;
}

.item3 {
  flex-shrink: 3;
  flex-basis: 100px;
}

.item4 {
  flex-shrink: 4;
  flex-basis: 150px;
}

Four items perfectly fit their parent block

To start calculation, let's find the value of space deficiency. Sum final widths of each item (flex-basis, margin-left and margin-right in our case) and reduce by the container width:

(100 + 5 + 5) + (200 + 5 + 5) + (100 + 5 + 5) + (150 + 5 + 5) – 400 = 190

Then calculate the sum of flex-shrink * final width of each item:

1 * (100 + 5 + 5) + 2 * (200 + 5 + 5) + 3 * (100 + 5 + 5) + 4 * (150 + 5 + 5) = 1500

And there is a formula for each item reducing: space deficiency * flex-shrink * final width of item / the last sum.

item1: 190 * 1 * (100 + 5 + 5) / 1500 = 13.9

item2: 190 * 2 * (200 + 5 + 5) / 1500 = 53.2

item3: 190 * 3 * (100 + 5 + 5) / 1500 = 41.8

item4: 190 * 4 * (150 + 5 + 5) / 1500 = 81

And finally calculate each item's width:

item1: 100 – 13.9 = 86.1

item2: 200 – 53.2 = 146.8

item3: 100 – 41.8 = 58.2

item4: 150 – 81 = 69

If you try the example and use developers tools in your browser, you will see that items' width values above appear with slight deviations. This is due to how the browser renders the page.

The bigger the value of the flex-shrink property, the bigger the reduction of the width.

If you have really read and understood the previous mathematical text, the author deeply admires you.

At first sight, it doesn't seem like both of the properties are used very often or used at all, but actually, there are a lot of examples of their usage! Even the main page of Google:

Normal buttons under the Google search line

If those two buttons were items of a flex container (maybe they are), they would probably look weird without flex-grow:

Buttons have no paddings

The contents of any item, as well as, for example, the number of the items, may change, which will lead to overflow or, otherwise, to items filling the container without overflow. And that implies a change of the CSS properties or even of the whole structure. With flex-grow or flex-shrink, you don't have to worry about manually changing the width of the items.

Flex

There is also a shorthand property flex, which combines all the properties listed above. You should use flex-grow as the first value, flex-shrink as the second value, and eventually flex-basis as the third one.

If none of the properties are set, the default values will be applied. For flex-grow it's 0, for flex-shrink it's 1, and for flex-basis it's auto, which is the size of the item's contents. There are some keyword values:

flex: initial; = flex: 0 1 auto;

flex: auto; = flex: 1 1 auto;

flex: none; = flex: 0 0 auto;

Conclusion

flex-grow and flex-shrink are the properties which are responsible for the width of the items. The size of an item by default is defined by either the width of its content or by the flex-basis value. If there is remaining space, it will be divided between the items in proportion to their flex-grow or flex-shrink values.

It turns out that these properties are not easy to use, especially when it comes to math and negative values. But the key word here is proportion, and if you bear it in mind, you'll successfully use these properties every time.

168 learners liked this piece of theory. 2 didn't like it. What about you?
Report a typo