Basic concepts of flex layout
The traditional solution to layout, based on the box model, relies on the display property + position property + float property. It is very inconvenient for those special layouts, for example, vertical centering is not easy to implement. In 2009, the W3C proposed a new scheme, Flex Layout, which can implement various page layouts simply, completely, and responsively. Currently, it is supported by all browsers, which means that this feature can now be safely used.
Of course, there is another layout solution is grid layout, but this article will sort out flex first.
The Flexible Box model, commonly referred to as flexbox, is a one-dimensional layout model. It provides powerful spatial distribution and alignment between the child elements of flexbox. This article gives the main features of flexbox, more details will be explored in other docs.
We say that a flexbox is a one-dimensional layout because a flexbox can only handle the layout of elements in one dimension at a time, one row or one column. As a contrast, another two-dimensional layout, CSS Grid Layout, can handle both row and column layouts.
The two axes of the flexbox
When using a flex layout, the first thing that comes to mind are two axes - the main axis and the cross axis. The main axis is defined by flex-direction, and the other axis is perpendicular to it. All the properties we use with flexbox are related to these two axes, so it is necessary to understand it first at the beginning.
Spindle
The spindle is defined by flex-direction and can take 4 values:
- row
- row-reverse
- column
- column-reverse
If you choose row or row-reverse, your spindle will extend along the inline, that is, horizontally.
When you choose column or column-reverse, your main axis will extend in the vertical direction - that is, the direction in which the blocks are arranged, that is, the vertical direction.
Cross axis
The cross axis is perpendicular to the main axis, so if your flex-direction is set to row or row-reverse, the direction of the cross axis is down the column.
If the main axis direction is set to column or column-reverse, the cross axis is horizontal.
Start and End Lines
Another important point to understand is that ** flexbox does not provide assumptions about the writing mode of the doc **. In the past, the writing mode of CSS was mainly considered horizontal, from left to right. Modern layouts cover the scope of writing modes, so we no longer assume that a line of text is written from the upper left corner of the doc to the right, and new lines do not have to appear below another line.
If flex-direction is row and I am writing English, then the start line of the main axis is on the left and the end line is on the right. If I am writing Arabic, the start line of the main axis is on the right and the end line is on the left. In both cases, the start line of the intersection axis is the top of the flex container and the end line is the bottom, because both languages are written horizontally.
Flex container
The area of the doc that uses flexbox is called a flex container. To create a flex container, we change the display property value of a container to flex or inline-flex. After this step, the immediate child elements in the container will become flex elements. All CSS properties will have an initial value, so all flex elements in the flex container will have the following behavior:
The elements are arranged in a row (the initial value of the flex-direction property is row).
- Elements start from the start line of the main axis.
- Elements are not stretched in the main dimension, but can be shrunk.
- Elements are stretched to fill the cross axis size.
- The flex-basis property is auto.
- The flex-wrap property is nowrap.
This will make your elements line up and use their own size as the size on the main axis. If there are too many elements outside the container, they will overflow without wrapping. If some elements are taller than others, the element will be stretched along the intersection axis to fill its size.
Implement multi-line container with flex-wrap
Although flexbox is a one-dimensional model, it is possible to apply our flex project to multiple rows. ** When doing this, you should treat each row as a new flex container **. Any spatial distribution will occur on that row without affecting other rows of that spatial distribution.
To achieve a multi-line effect, add a property value wrap to the property flex-wrap. Now, if your item is too large to fit in one line, it will wrap.
For flex containers, the total width of the child elements of the item is greater than the maximum width of the container. Since the value of flex-wrap is set to wrap, the child elements of the item wrap. If you set it to nowrap, which is also the initial value, they will shrink to fit the container because they use the initial Flexbox value that allows for shrinking. If the child elements of the item cannot be shrunk, using nowrap will cause overflow, or the shrunk level is not small enough
Abbreviated attribute
You can combine two properties flex-direction and flex-wrap into a short property flex-flow. The first specified value is flex-direction and the second specified value is flex-wrap.
Attributes on flex elements
To better control flex elements, there are three properties that can be applied to them:
- flex-grow
- flex-shrink
- flex-basis
Before considering the role of these properties, it is necessary to understand the concept of ** available space **. The role of these flex properties is to change the behavior of available space in the flex container. At the same time, available space is also important for the alignment behavior of flex elements.
Assuming that in a 500px container, we have 3 elements that are 100px wide, then these 3 elements need to occupy 300px of width, leaving 200px of free space. By default, flexbox behavior leaves this 200px of space behind the last element.
If we expect these elements to automatically expand to fill the remaining space, then we need to control how the available space is allocated among these elements, which is what the flex attribute on the element does.
flex-basis
The flex-basis defines the size of that item in terms of the space, and the extra space in the flex container other than the space occupied by the element is available space. The default value of this attribute is auto. At this point, the browser will detect whether the element has a certain size. In the above example, all elements have ** set the width to 100px, so the flex-basis value is 100px **.
** If no dimensions are given to the element, the flex-basis value takes the size of the element’s content **. This explains: as long as we declare display: flex to the parent element of the Flex element, all child elements will be lined up and automatically assigned a size to fully display the content of the element.
flex-grow
If flex-grow is assigned a positive integer, the flex element will grow in size along the main axis based on flex-basis. This will make the element stretch and occupy the available space on this axis. If other elements are also allowed to stretch, they will each occupy a portion of the available space.
If we set flex-grow to 1 for all the elements in the example above, the available space in the container will be divided equally between these elements. They will stretch to fill the space in the direction of the container spindle.
The flex-grow property allocates space proportionally. If the first element has a flex-grow value of 2 and the other elements have a value of 1, the first element will occupy 2/4 (in the above example, 100px out of 200px), and the other two elements will occupy 1/4 each (50px each).
flex-shrink
The flex-grow property handles the flex element adding space on the spindle, while the flex-shrink property handles the flex element contraction. ** If we don’t have enough space in our container to arrange flex elements, we can set the flex-shrink property of the flex element to a positive integer to shrink its space below flex-basis **. As with the flex-grow property, different values can be assigned to control the degree of contraction of the flex element - a larger value can be assigned to the flex-shrink property to a greater degree of contraction than the sibling element assigned a decimal value.
When calculating the contraction size of the flex element, its minimum size is also taken into account, which means that in fact the flex-shrink property may not behave the same as the flex-grow property.
Flex property abbreviation
You may rarely see the flex-grow, flex-shrink, and flex-basis properties used alone, but mixed in the flex shorthand. The flex shorthand allows you to write three values in this order - flex-grow, flex-shrink, flex-basis.
In most cases, you can use predefined shorthand forms. You may see this writing frequently in this tutorial, and you can use it in many cases. Here are several predefined values:
- flex: initial
- flex: auto
- flex: none
- flex:
Flex: initial is the initial value for resetting flex elements to Flexbox, which is equivalent to flex: 0 1 auto. Here flex-grow has a value of 0, so flex elements do not exceed their flex-basis size. Flex-shrink has a value of 1, so flex elements can be shrunk to prevent them from overflowing. Flex-basis has a value of auto. Flex element size can be set in the main dimension or automatically obtained based on content.
Flex: auto is equivalent to flex: 1 1 auto; basically the same as flex: initial above, but in this case the flex element can be stretched or contracted when needed.
Flex: none sets a flex element to be non-scalable. It is the same as setting flex: 0 0 auto. Elements cannot be stretched or contracted, but elements are laid out as flexbox with flex-basis: auto.
You often see flex: 1 or flex: 2 in tutorials, etc. It is equivalent to flex: 1 1 0. Elements can be scaled on a flex-basis of 0.
Alignment and space allocation of elements
A key feature of Flexbox is the ability to set the alignment of flex elements along the spindle and cross axes, as well as the allocation of space between them.
align-items
The align-items property aligns elements in the cross axis direction.
The initial value of this attribute is stretch, which is why flex elements are stretched to the height of the tallest element by default. In effect, they are stretched to fill the flex container - the tallest element defines the height of the container.
You can also set the value of align-items to flex-start, so that flex elements are aligned top of the flex container, flex-end aligns them bottom of the flex container, or center aligns them center. Try it in the example - I gave the height of the flex container so you can see the elements moving through the container.
- stretch
- flex-start
- flex-end
- center
justify-content
The justify-content property is used to align elements in the main axis direction, which is the direction set by flex-direction. The initial value is flex-start, and the elements are arranged from the start line of the container. But you can also set the value to flex-end, which is arranged from the end line, or center, which is arranged in the middle.
You can also set the value to space-between to take out the remaining space after the elements are arranged and distribute it evenly between the elements, so that the spacing between elements is equal. Or use space-around to make the left and right spaces equal for each element.
The values of the following justify-content attribute:
- stretch
- flex-start
- flex-end
- center
- space-around
- space-between
Proportion of child elements on the main axis
In HTML, if the CSS of an element is set to display: flex, then the element is a flex container and its child elements are flex items
We will explore three properties applied to flex child elements that allow us to control the size and scalability of flex child elements in the spindle direction - flex-grow, flex-shrink, and flex-basis. Fully understanding how these properties work with growing and shrinking flex child elements is the key to mastering Flex layout.
These three properties control the following aspects of a flex child element:
- flex-grow: How much positive free space does this flex child element get?
- flex-shrink: How much negative free space is to be contracted from this flex child element?
- flex-basis: What is the size of the flex child element before it is stretched and contracted?
Important concept of working on the spindle
Before considering how the flex property controls the ratio in the spindle direction, there are a few concepts worth exploring. This involves the natural size of flex child elements before any scaling, and the concept of free space
Size of Flex child elements
In order to figure out how much free space can be laid out in the flex child element, the browser must know how big the item is to start. How does it solve for flex child elements with no width and height applied to absolute units?
There is a concept in the CSS of min-content and max-content - these keywords are defined in the CSS Intrinsic and Extrinsic Sizing Specification, and can be replaced by a length unit.
For example, I have two paragraphs that contain a text string. The first paragraph sets the width of the min-content. In browsers that support this keyword you can see that the text has been wrapped as much as possible, as small as possible without overflow. After that is the min-content size of that string. Essentially, the longest word in the string determines the size.
The second paragraph sets the max-content value, and it is the opposite of min-content. It will become as large as possible, with no automatic line wrapping. If the flex container is too narrow, it will overflow its own box.
Positive and negative free space
Positive and negative free space literally translates to positive and negative free space. In order for readers to read the following well, the translator will probably say it first.
There are flex containers and flex child elements in the Flex layout. The flex child elements are contained in the flex container. Then when the sum of the dimensions (sizes) of the flex child elements on the spindle is smaller than the size of the flex container, there will be a flex container. There will be extra space that is not filled, and these spaces are called positive free space. When the sum of the dimensions of the flex child elements on the spindle is greater than the size of the flex container, the space of the flex container is not enough. At this time, the sum of the dimensions of the flex child elements minus the size of the flex container (the size of the overflow of the flex child element) is negative free space, this negative free space plus the size of the flex container is just enough to accommodate the flex child element.
Before talking about these properties we need to understand the concept of positive free space and negative free space. When a flex container has positive free space, it has more space for displaying flex child elements within the flex container. For example, if I have a 500px wide flex container with a flex-direction property value of row, three 100px wide flex child elements, and I have 200px positive free space, then if I want them (positive free space) to fill the flex container, they can be filled between flex child elements.
We produce negative free space when the natural size of the flex child elements adds up to more than the available space inside the flex container. If I have a flex container 500px wide like the above, but three flex child elements are each 200px wide, I need a total of 600px wide, so I have 100px negative free space. This can be removed from the flex child element to make it fit into the flex container.
flex-basis
The flex-basis property initializes the size of the flex child element before any space allocation occurs. The initial value of this property is auto. If flex-basis is set to auto, the browser checks whether the main size of the flex child element is set to an absolute value before calculating the initial value of the flex child element. For example, if you have set your flex child element to 200px wide, 200px is the flex-basis of this flex child element.
If your flex child element is automatically resized, auto will resolve to the size of its content. At this point, the familiar min-content and max-content sizes will become useful, and flexbox will use the max-content size of the flex child element as the flex-basis. The following example can prove this.
In addition to the keyword auto, you can also use the keyword content as the value of flex-basis. This will cause flex-basis to set the width of even the flex child element based on the content size. This is a new keyword and has less browser support, but you can still achieve the same effect by setting flex-basis: auto and making sure your flex child element has no width set so that it can automatically resize.
When allocating space, if you want flexbox to completely ignore the size of the flex child element, set the flex-basis to 0. This basically tells flexbox that all space can be preempted and shared proportionally.
flex-grow
The flex-grow property specifies the flex growth value, which determines how much the flex child element grows relative to the rest of the flex child elements in the flex container when positive free space is allocated.
If all your flex child elements have the same flex-grow property value, then the space will be divided equally among the flex child elements. If you want this to happen, you usually need to use 1 as the value, and if you like you can also set their flex-grow to 88, or 100, or 1.2 - it’s just a ratio. If all flex-grow values are the same, and there is positive free space in the flex container, then it will be equally distributed to all flex child elements
Combined flex-grow
Some things can get confusing depending on how flex-grow and flex-basis interact. Let’s consider three examples of flex child elements with different content sizes, applied to the following flex rules:
flex: 1 1 auto;
In this example, the flex-basis is set to auto and their width is not set, so they are automatically resized. This means that the size of the flexbox depends on the max-content size of all flex child elements. After the flex child elements are laid out, there are some positive free spaces in the flex container, which are displayed in the shaded area of this image:
We use a flex-basis equal to the content size in order to subtract the available allocation space from the total available space (the width of the flex container), and then the remaining space is evenly distributed among each flex child element. Our larger flex child element ends up being bigger because it starts out with a larger size, even though it has the same amount of allocated space as other flex child elements:
** If all you really want is three flex child elements of the same size, even if they start out as different sizes **, you should use this:
flex: 1 1 0;
What we are saying is that for our space allocation, the flex child element has a size calculation of 0 – all spaces are up for grabs, and all flex child elements have the same flex-grow value, and they (flex child elements) each get equal space allocation. The end result is three scalable flex child elements of equal width.
flex
Our understanding of how flex-grow and flex-basis work allows us to further control our individual flex child elements by assigning different flex-grow values, sizes. If we set the flex-basis value to 0 then all the space used can be allocated, and we can assign different flex-grow values to each flex child element. In the example below, I will use the following values:
Set the flex-grow value of the first flex item to 1.
- Set the flex-grow value of the second flex item to 1.
- Set the flex-grow value of the third flex item to 2.
A flex-basis value of 0 means that the available space will be allocated according to the settings. We need to increase the flex growth value, we need to divide the total size of positive free space in the flex container by the sum of the flex-grow values, in this case 4. We can allocate space based on individual values (flex-basis values) - the first flex child element gets one unit, the second flex child element gets one unit, and the third flex child element gets two units. That is, the third flex child element is twice as large as the first and second flex items.
Flex-shrink property
The flex-shrink property specifies the flex shrinkage value, which determines the degree of contraction of the flex child element relative to the remaining flex child elements in the flex container when allocating negative free space.
This property handles situations where the browser calculates flex-basis values for flex child elements and detects that they are too large to fit the flex container. ** As long as flex-shrink has a positive value, flex child elements will contract so that they will not overflow the flex container **.
So flex-grow is used to add free space, and flex-shrink reduces space to fit boxes into their containers without overflowing.
In one example, my flex container has three flex child elements, and I have set each of them (flex child elements) to be 200px wide and the container (flex container) to be 500px wide. Set flex-shrink to 0 for flex child elements that do not allow contraction such that they overflow the box.
Change the flex-shrink value to 1 and you will see that each flex child element has a contraction of the same size, and now all flex child elements fit into the box. In doing so they have become smaller than their original width.
Combining flex-shrink and flex-basis
You can see that flex-shrink and flex-grow work just as well. But they are not exactly the same for two reasons.
Even if it is trivial, the definition in the specification is one reason why flex-shrink is not identical to negative space, just as flex-grow is not identical to positive space:
When assigned
The second reason is that flexbox prevents small flex child elements from shrinking to 0 during negative free space elimination. These flex child elements will be laid with the size of min-content - the size they will become after taking advantage of any available automatic line break opportunities.
In the example below, you will see min-content laid where flex-basis resolves to content size. If you change the width of the flex container - say increase it to 700px wide, and then reduce the width of the flex child element, you will see that the first two flex child elements will wrap, but they will never be smaller than the size of min-content. As the box gets smaller and smaller, the third flex child element then overflows from the space.
1 | .box { |
1 | <div class="box"> |
Master
The key to really understanding how flex child elements work is to understand how many things are involved in influencing flex child elements. Consider the following aspects, which we have discussed in these guides:
What settings
- is lex-basis set to auto, and is the width of this flex child element set? If set, the size of the flex child element will be based on the set width.
- flex-basis set to auto or content (in supported browsers)? If auto, the size of the flex child element is the original size.
- is flex-basis a length unit other than 0? if so this is the size of the flex child element.
- what about setting flex-basis to 0? If so, the size of the flex child element is not considered in the space allocation calculation.
Do we have space available?
A flex child element will not grow without positive free space and will not shrink without negative free space.
- If we add up the widths of all the flex child elements (height if working in column direction), is the sum less than the total width (or height) of the flex container? If so, then you have positive free space and flex-grow comes into play.
- If we add up the widths of all the flex child elements (height if working in column direction), is the sum greater than the total width (or height) of the flex container? If so, then you have negative free space and flex-shrink comes into play.
Other ways to allocate space
If we don’t want to add space to flex child elements, remember that you can use the alignment attribute described in the guide in the flex container to handle free space between or around flex child elements so that you can align flex child elements. The justify-content attribute allocates free space between or around flex child elements. You can also use auto margins on flex child elements to absorb space and create spacing between flex child elements.
Reference article: