Layouts in CSS: Creating Flexible Layouts with Flexbox

The CSS Flexible Box Layout Module , better known as Flexbox, was designed to distribute elements and space in one direction—a row ( fLex-direction: row or row-reverse ) or a column ( fLex-direction: column or column-reverse ).

A basic flexible box layout is simple to create: just add display: flex or display: inLine- fLex to the containing element. These values for display trigger a flex formatting context for that containing element’s children. Both flex and inline-flex are inner display modes. We set these values on the container, which behaves like a block-level or inline-level box, respectively. The children of that container are then arranged according to the rules of flex layout.

By adding display: flex or display: inline-flex to a containing element, its immediate children become flex items. Flex items may be element children or non-empty text nodes, as shown in the following example:

<div style=”display: flex; width: 1000px; margin: 5rem auto;”>

<span>This text is contained by a SPAN element.</span>

<b>This text is contained by a B element.</b>

This un-wrapped text node still behaves like a flex item.

</div>

Flex items don’t have to be elements. Non-empty text nodes can also be flex items. The code above produces the layout shown below.

Let’s look at an example using the markup below. Notice here that the contents of .alpha , .beta , .gamma , and .delta differ in size:

<div class=”flex-container”>

<div class=”alpha”>

<b>A</b>

</div>

<div class=”beta”>

<b>B</b>

<p>This is a short sentence made of short words.</p>

<p>Antidisestablishmentarianism is a long word.</p>

</div>

<div class=”gamma”>

<b>C</b>

<img src=”400×300.png” width=”400″ height=”300″ alt=”placeholder”>

</div>

<div class=”delta”>

<b>D</b>

</div>

<div class=”epsilon”>

<b>E</b>

</div>

</div>

The CSS we’ll use is simple. We’ll add a display: flex declaration to .flex-container , and set its inline size to 1500 pixels:

.flex-container {

display: flex;

inline-size: 1500px; /* Can also use width: 1500px */

}

The image below shows the result: a simple flexible box layout with display: flex applied, and no other Flexbox properties. The dashed line represents the boundaries of our flex container. Each flex item is about as wide as its contents, plus whatever padding exists inside the flex item.

This example uses the initial value of flex , which is 0 1 auto . The flex property applies to the children of a flex container. Since the flex property sits at the heart of Flexbox, it’s important to understand how it works.

1. Understanding the flex Property

The flex property is actually a shorthand for three other properties:

  • flex-grow indicates the factor by which an element should grow, if necessary, and must be a positive integer. Its initial value is 0 .
  • flex-shrink indicates the factor by which that an element should shrink, if necessary, and must be a positive integer. Its initial value is 1 .
  • flex-basis: indicates the initial or minimum size of a flex item. This may be its width when the main axis is horizontal (for example, with flex-direction: row ), or the flex item’s height when the main axis is vertical. (See the note below for more detail on this.) The value of flex-basis must be a valid value for the width property.
  • when fLex-direction: row , the main axis is horizontal, and the main size is the flex item’s width
  • when fLex-direction: column , the main axis is vertical, the main size is the flex item’s height

For vertical writing modes:

  • when fLex-direction: row , the main axis is vertical, and the main size is the flex item’s height
  • when fLex-direction: column , the main axis is horizontal, and the main size is the flex item’s width

When fLex-basis is auto , the maximum size of the flex item becomes its initial size, also known as the flex base size. In the example above, .gamma is 400 pixels wide because one of its children is 400 pixels wide. Similarly, .alpha (A), .beta (B), .delta (D), and .epsilon (E) are as wide as their longest line of text. For .beta , that’s 389.633 pixels. For .alpha , .delta , and .epsilon , that ranges from 37.333 to 47.733 pixels wide, depending on the width of the letters A, D, and E.

The value of each flex item’s fLex-basis determines whether the browser will use fLex- grow or fLex-shrink when allocating free space within the flex container. When the sum of the flex base size for each flex item is greater than the inner size of the flex container, the browser allocates space using fLex-shrink . If it’s less than the inner size of the flex container, it uses fLex-grow . This is the used flex factor.

Let’s add the flex basis values of our flex items from the example above:

47.733 + 386.663 + 400 + 43.983 + 37.333 = 915.712

Remember that our container is 1500 pixels wide. Since 915.712 pixels is less than 1500 pixels, the browser uses the fLex-grow value to allocate free space inside the container; fLex-shrink is ignored.

In this case, the initial flex: 0 1 auto declaration means that each flex item grows from its initial size by a factor of zero. In other words, it won’t grow at all.

2. Using the flex Property

Although it’s possible to set fLex-grow , fLex-shrink , and fLex-basis individually, the specification recommends using the flex shorthand property. It accepts one, two, or three values.

When using one-value syntax, the value must be a number or one of the initial , auto , or none keywords. When the value is a number-say, flex: 4 —this value is interpreted as the value for fLex-grow . The fLex-shrink value is assumed to be 1 , which is its initial value. However, the value of fLex-basis is assumed to be 0 , instead of its initial value of auto . In other words, flex: 4 is the equivalent of flex: 4 1 0 and not flex: 4 1 auto . That seems counterintuitive at first, but changing the value of fLex-basis to 0 makes fLex-grow and fLex-shrink behave more predictably.

When using a two-value syntax, the first value must be a number. Here, too, it’s interpreted as the value of fLex-grow . The second value, however, can be either a number or a valid value of the width property.

If the second value is a number, it’s interpreted as a value of fLex-shrink , and the value of fLex-basis is assumed to be 0 . If it’s a width value, it’s interpreted as the fLex-basis value, and fLex-shrink is assumed to be 1 .

To explain it another way, take this CSS:

.item {

flex: 4 1;

}

The code above is the equivalent of this:

.item {

flex-grow: 4;

flex-shrink: 1;

flex-basis: 0%;

}

On the other hand, take this CSS:

.item {

flex: 2 auto;

}

It’s equivalent to this:

.item {

flex-grow: 2;

flex-shrink: 1;

flex-basis: auto;

}

When using the three-value syntax, values are interpreted as flex-grow , flex-shrink , and flex-basis , in that order. The first two values must be numbers. The last value must be a valid value for the width property.

3. Flex Factors and Space Distribution

Both flex-grow and flex-shrink represent proportions. They tell the browser how to allocate the free space inside of a flex container.

So how does the browser determine the free space? First, it adds the flex base size of every flex item and deducts that from the inner width of the flex container. Then it subtracts the size of inflexible items. An item is considered inflexible if:

  • its flex factor is zero
  • its flex base size is greater than its hypothetical main size, and the browser is using the flex shrink factor
  • its flex base size is smaller than its hypothetical main size, and the browser is using the flex grow factor

The space that remains is the free space. Keep in mind that both gap and margin can further reduce the amount of free space in a container.

Let’s return to our previous example. We’ll add an explicit value for flex to all of our flex items:

.flex-container > div {

flex: 1;

}

The image below shows the result.

Adding flex: 1 to a flex item is the equivalent of adding flex: 1 1 0 . However, .gamma img has an intrinsic width of 400 pixels, so .gamma will be at least 400 pixels wide. Since the browser already knows what size .gamma needs to be, the browser subtracts its width from that of the container: 1500 minus 400 equals 1100 pixels of free space.

Once the browser has determined the free space available, it calculates the main size of each flexible item:

  • If the flex used factor is flex-grow , the calculation is roughly: free space + sum of each flexible item’s flex-grow value x flex-grow .
  • If the flex used factor is flex-shrink , the calculation is roughly: flex base size – ((free space ÷ sum of each flexible item’s flex-shrink value) × flex-shrink ) 

Gaps and margins also affect size of flex items. Margins don’t collapse in a flex formatting context. Instead, margins along the main axis are deducted from the main size of each flex item. Gaps set using the row-gap , coLumn-gap or gap properties, work similarly. Each gap reduces the size of a flex item by one half of the gap length.

The specification details a far more complex process for this, but for the purposes of this book, the explanation above will do.

Let’s apply this formula to our current example. We have four items with an indefinite or undetermined width, and a fLex-grow factor of 1 :1100 + (1 +1 +1 +1) = 275. For each flex item, multiply 275 by the value of that item’s fLex-grow property. In this case, all of our items will be 275 pixels wide.

Here’s another example. We’ll set the fLex-grow value of .epsilon to 5. It inherits the fLex- shrink and fLex-basis value of the .fLex-container > div rule set:

/* Remember this is the same as flex: 1 1 0; */

.flex-container > div {

flex: 1;

}

.flex-container > .epsilon {

flex-grow: 5;

}

You can see the result in the image below. The fLex-grow factor of 5 changes the ratio by which space gets distributed within the flex container.

Since “Antidisestablishmentarianism” is one (long) word, it forces .beta to be 249 pixels wide. In this case, the browser deducts the width of .beta and .gamma from the inner width of the flex container: 1500 – (400 + 249) ~ 851. Our flex container has 851 pixels of free space to distribute.

Let’s determine the size of our flexible items. We’ll add the flex factors for .alpha , .delta , and .epsilon , then divide our free space value by that number: 851 + (1 +1 + 5) = 121.571. Now we can multiply this number by our flex grow factor to determine the size of each flex item:

  • .alpha : 121.571 x 1121.571 m
  • . beta : 121.571 x 1 = 121.571 m
  • . epsilon : 121.571 ><5 = 607.855

Flex item .epsilon is roughly five times as wide as .alpha and .beta .

4. When Flex Items Shrink

When the chosen flex mode is flex-shrink , the browser determines how much space to shrink each flex item by. Remember, flex items shrink when their initial main size exceeds the inner main size of the container. Let’s change the flex-basis value of our flex items from 0 to 410px . We’ll also give .epsilon a flex-shrink factor of 5:

.flex-container > div {

flex: 1 1 410px;

}

.flex-container > .epsilon {

flex-shrink: 5;

}

This makes the sum of the hypothetical main size of each flex item 2050 pixels—greater than the 1500 pixel width of our container. First, determine the amount of free space available: 1500 – 2050 = -550 pixels.

Next, account for the width of .gamma img . It’s still 400 pixels wide, which means .gamma shrinks by ten pixels—the difference between its contents and flex-basis . Deduct that ten- pixel difference from our free space: -550 +10 = -540 pixels.

We’ve accounted for the width of .gamma , so we can perform the next step. Divide the amount of free space by the sum of the flex factors of our flexible items, .alpha , .beta , .delta , and .epsilon : -540 + (1 +1 +1 + 5) ~ -67.5.

Now we can calculate the size of each flex item:

  • .alpha : 410 + (-67.5 x 1) = 342.5 pixels U
  • .beta : 410 + (-67.5 xl) = 342.5 pixels
  • .delta : 410 + (-67.5 x 1) = 342.5 pixels M
  • .epsiLon : 410 + (-67.5 x 5) = 72.5 pixels

The image below shows the result. When the chosen flex factor is fLex-shrink , the browser shrinks flex items proportionately.

5. Creating Multi-line Flexible Layouts

So far, our examples have looked at flex box spacing in a single direction, along a single line. We can also make flex items wrap across multiple lines using the fLex-wrap property. Its initial value is nowrap . Other values are wrap and wrap-reverse .

The spacing formulas for fLex-grow and fLex-shrink work per line. Flex items wrap and form a new line when the sum of their hypothetical main size exceeds the inner main size of the flex container. Take the following CSS:

.flex-container {

display: flex;

width: 1500px;

gap: 20px;

flex-wrap: wrap;

}

.flex-container > div {

flex: 0 1 33.333%;

}

Since flex-basis is 33.33%, each flex item should All one third of the available space. But this time, we’ve added a gap of 20 pixels. Although the sum of the hypothetical main size of these flex items is 1500 pixels—33.333% of 1500 is roughly 500 pixels, and 500 + 500 + 500 = 1500—the additional gap changes how many items can fit on a line—as illustrated below.

Only two flex items fit on each line. Because our flex-grow value is zero, these flex items don’t expand to fill the width of our flex container. If we changed the declaration to flex: 1 1 33.333%; , they would.

The wrap-reverse value works the same way as wrap , but reverses the visual ordering of flex items. The following image shows how flex-wrap: wrap-reverse changes a flex layout, reversing the visual ordering of the container’s children.

The examples we’ve used thus far have looked at horizontal spacing. But in some circumstances, you may want to distribute space vertically. We’ll discuss the flex-direction property in the next section.

6. Distributing Space Vertically with flex-dinection

The flex-direction property lets us change the main axis direction of our flex container. Its initial value is row ; other values are column and column-reverse . The following image shows a flex container with a flex-direction value of column , which changes the axis along which space space gets distributed.

The title of this section is a little misleading. The fLex-coLumn also depends on the writing mode of the document. In languages that are written and read horizontally, columns are vertical, as shown by the image above. Vertical languages rotate the row and column axes by 90 degrees clockwise (as with writing-mode: verticaL-rL ) or counterclockwise (as with writing-mode: verticaL-Lr ) . Consider the CSS below:

.flex-container {

display: flex;

flex-direction: column;

writing-mode: vertical-rl;

}

This code creates the layout pictured below.

When using fLex-direction: column with multilingual sites, the block-size property is a better choice than height or max-height . Remember that block-size and inline-size are relative to the writing mode, rather than to the vertical and horizontal dimensions as height and width are. Using logical properties removes the need to reset properties based on the document’s language.

Source: Brown Tiffany B (2021), CSS , SitePoint; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *