Introducing CSS Flexbox
In this section, we’re going to learn about a set of features and capabilities which are collectively known as CSS flexbox. They’re defined in something called the CSS Flexible Box Layout Module, hence “flexbox”; the syntax was finalised around 2012, and achieved stable support across all mainstream browsers by 2017.
I spent a lot of time thinking about where to introduce flexbox in this course. There’s one pretty strong argument that flexbox is the de facto standard layout model for modern CSS, and that if you know how to use flexbox effectively, you probably never need to use a lot of the things we’ve already seen, like floats and layout grids — and so we could have learned about flexbox right at the beginning, used it for all the other demos and exercises, and skipped over all the other stuff entirely.
There are three reasons I didn’t do that. First: you’re going to find that stuff in the wild. Sure, if you’re building a brand new greenfield web application starting today, you’re probably going to build your layouts around flexboxes… but most of us aren’t building greenfield applications, we’re maintaining sites and systems which have been around for a while.
Second: I think taking the time to look at block and inline elements, floats, and layout grids provides a much better understanding of why flexbox was created, and what sort of problems it was intended to solve.
Third: flexbox is mind-meltingly complicated. It’s a completely new layout model, with ten new properties to consider, all of which can interact with each other in complex and sometimes unexpected ways… for folks who’ve not worked with CSS before, it’s a little overwhelming. But if you’ve already spent a bit of time using things like layout grids, flexbox will be like a breath of fresh air. Albeit slightly complicated air with a lot of interesting smells in it.
You ready? Let’s see what it can do.
Flexbox Fundamentals
Flexbox is designed to align elements in a container along a single axis; every flexbox container is fundamentally trying to be either a single row of things, or a single column of things. This is in contrast to the CSS Grid we’ll meet a little later, which is all about two-dimensional layouts.
Also bear in mind that while you can use flexbox to lay out entire screens and applications, it’s just as effective for building small standalone components, as we’ll see later in this section.
To activate the flexbox layout system, you’ll need a container element with display: flex on it — or display: inline-flex, if you want your container to behave like an inline element.
That’ll give you a container with a main axis and a cross axis, and just about everything else is defined in terms of those two axes.
<section style="display: flex;">
<span>A</span>
<span>B</span>
<span>C</span>
</section>
<section style="display: flex; flex-direction: column;">
<span>A</span>
<span>B</span>
<span>C</span>
</section>
<section style="display: flex; flex-direction: row-reverse;">
<span>A</span>
<span>B</span>
<span>C</span>
</section>
<section style="display: flex; flex-direction: column-reverse;">
<span>A</span>
<span>B</span>
<span>C</span>
</section>By default, flex will try to fit every element onto the same row (or column), shrinking elements as necessary. Use flex-wrap if you want items to wrap across multiple lines
<pre>flex-wrap: nowrap (default)</pre>
<div style="display: flex; flex-wrap: nowrap">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>
<pre>flex-wrap: wrap;</pre>
<div style="display: flex; flex-wrap: wrap">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>
<pre>flex-wrap: wrap-reverse;</pre>
<div style="display: flex; flex-wrap: wrap-reverse">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>You can also set the direction and wrap in one statement using the
flex-flowshorthand property:flex-flow: <direction> <wrap>;
Mind the Gap
Flexbox layouts will respect element margins, but you can also specify a gap between the elements in a flex container using row-gap, column-gap, or the shorthand gap property.
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flex Wrap</title>
<style>
div {
display: flex;
flex-wrap: wrap;
background-color: royalblue;
}
span {
width: 3rem;
border: 1px solid crimson;
padding: 5px;
text-align: center;
background-color: gold;
text-align: center;
}
</style>
</head>
<body>
<pre>row-gap: 1rem;</pre>
<div style="row-gap: 1rem;">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>
<pre>column-gap: 1rem;</pre>
<div style="column-gap: 1rem;">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>
<pre>gap: 1rem;</pre>
<div style="gap: 1rem;">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>
<pre>gap: 1rem 3rem;</pre>
<div style="gap: 1rem 3rem;">
<span>A</span><span>B</span><span>C</span><span>D</span><span>E</span>
<span>F</span><span>G</span><span>H</span><span>I</span><span>J</span>
<span>K</span><span>L</span><span>M</span><span>N</span><span>O</span>
<span>P</span><span>Q</span><span>R</span><span>S</span><span>T</span>
<span>U</span><span>V</span><span>W</span><span>X</span><span>Y</span>
<span>Z</span>
</div>
</body>
</html>Notice that the gap is only applied between items; if you want a gap between the container and the items, either set padding on the container, or add a margin to the elements.
Justify and Align, Content and Items
This is where flexbox starts to get a bit gnarly, mainly because we just don’t have enough specific words in everyday English for the number of different ways content inside a flexbox can be arranged.
I find it helpful to think of flexbox as three separate things - the container, the content, and the items. The container and items are the actual HTML elements, the content is the implied grouping created by the arrangement of the items:
justify-content controls how elements are arranged along the main axis, if they don’t fill the container.
There are several values there which appear to do the same thing, but remember that not all web pages will read from left to right:
leftandrightare fixed directions and are not affected by the document’s writing direction.startandendwill change based on the writing direction; in right-to-left reading systems like Hebrew and Arabic,startis the right,endis the left.flex-startandflex-endare relative to theflex-direction— but this is also based on the writing direction; in a left-to-right reading system,rowgoes left to right, androw-reversegoes right-to-left, so as far as flexbox is concerned,startis always the same asflex-startandendis always the same asflex-end.
Remember, too, that this is justification along the main axis. You change the axis orientation or direction, it’s going to produce a different effect:
and if you reverse the flex direction, the start becomes the end, so to speak:
To control how elements are laid out along the cross axis, use align-items:
If your flexbox container wraps, you’ll end up with more than one row (or column) of items; to control how these wrapped rows are laid out, use the align-content property. Here’s how it works with flex-direction: row:
and here’s how it works with flex-direction: column:
The Holy Grail of CSS
For many years, perfectly centring an element in its parent container has been a “Holy Grail” of CSS; horizontal alignment has been trivial since HTML 1.0, but to centre something vertically, developers had to resort to all sorts of tricks involving table cells, negative margins, JavaScript… well, no more. With the advent of flexbox, we can, finally, centre content within its container without resorting to hacks!
body, html { margin: 0; padding: 0; }
body {
display: flex;
height: 100vh;
}
div {
background-color: black;
width: 20vw;
height: 20vw;
text-align: center;
font-size: 14vw;
margin: auto;
}You can also use flexbox along with justify-content, align-items and text-align to centre text within its container without having to wrap it in an enclosing element:
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Centred Text</title>
<style>
body, html { margin: 0; padding: 0; }
body {
display: flex;
height: 100vh;
justify-content: center;
align-items: center;
text-align: center;
}
</style>
</head>
<body>
clowns to the left of me,<br>
jokers to the right,<br>
here I am...<br>
stuck in the middle with you.
</body>
</html>If you’ve only recently started working with CSS, you’re probably thinking “so what’s the big deal?” — but trust me, for those of us who’ve been around for a while, this will never stop being amazing.
Flexbox Item Properties
All the properties we’ve looked at above apply to the container - the element with the display: flex property applied to it, which activates the flexbox layout module.
We can also target individual flexbox items, which comes in useful when we’ve got one or two elements that should be laid out differently to the rest.
Flex Order
One very powerful, and potentially dangerous, application of CSS is causing elements to appear on the screen in a different order to how they appear in the underlying markup. We’ve already seen examples of this using position: absolute and float, to pull an element out of the regular document flow and render it somewhere else on the page. Flexbox goes a step further with this and gives us the order property, which will literally change the rendering order of elements in a container. Every item in a flex container has a default order of 0; giving items a non-zero order will override the order in which they appear in their container. Use a negative order to move an element nearer the start of the main axis:
body {
display: flex;
gap: 1rem;
background-color: royalblue;
}
div {
background-color: gold;
padding: 5px;
}
<div style="order: 1;">AC/DC (1)</div>
<div>Bon Jovi (0)</div>
<div style="order: 2;">Cinderella (2)</div>
<div style="order: -1;">Def Leppard (-1)</div>
<div>Europe (0)</div>Align Self
Individual flex items can also override the default item alignment for their flex container:
body {
display: flex;
gap: 10px;
}
section {
flex: 1;
display: flex;
padding: 5px;
gap: 5px;
min-height: 14em;
background: royalblue;
font-family: Consolas, 'Courier New', Courier, monospace;
font-size: 0.6rem;
div {
flex: 1;
background: gold;
padding: 5px;
}
}
<section style="align-items: start;">
<div>(default)</div>
<div style="align-self: end;">align-self: end</div>
<div style="align-self: stretch;">align-self: stretch</div>
<div style="align-self: center;">align-self: center</div>
</section>Watch out for using align-self with baseline values. Baseline alignment doesn’t align elements relative to the container, it aligns them with each other, and so it doesn’t make sense to specify baseline alignment for a single element. A collection of elements in the same flex container with the same baseline alignment is known as a baseline sharing group; in this example there are two such groups, one aligned to the default baseline, and one aligned to the last baseline:
section {
display: flex;
padding: 5px;
gap: 5px;
min-height: 18em;
background: royalblue;
div {
flex: 1;
background: gold;
padding: 5px;
&:nth-of-type(1) { font-size: 0.5rem; }
&:nth-of-type(2) { font-size: 0.7rem; }
&:nth-of-type(3) { font-size: 0.9rem; }
&:nth-of-type(4) { font-size: 1.2rem; }
&:nth-of-type(5) { font-size: 1.5rem; }
&:nth-of-type(6) { font-size: 1.8rem; }
&:nth-of-type(7) { font-size: 2.1rem; }
&:nth-of-type(8) { font-size: 2.4rem; }
}
hr {
position: absolute;
left: 0;
right: 0;
height: 0;
border-width: 1px 0 0 0;
border-style: dashed;
border-color: black;
&:first-of-type {
top: 1.7rem;
}
&:last-of-type {
bottom: 0.6rem;
}
}
<section style="align-items: baseline; position: relative;">
<hr>
<div>Always</div>
<div style="align-self: last baseline;">Born To Be My Baby</div>
<div>Carry On My Wayward
Son</div>
<div style="align-self: last baseline;">Dreams</div>
<div>Easy Come, Easy Go</div>
<div>Fear of the Dark</div>
<div style="align-self: last baseline;">Get Ready</div>
<div style="align-self: last baseline;">Have A Nice Day</div>
<hr>
</section>Flex Grow, Shrink and Basis
To control how the layout engine will resize an item, we can use flex-grow, flex-shrink and flex-basis, and this is where some seriously complex mathematics gets involved.
Let’s start with flex-basis . The flex-basis property works like width or height, but it’s aligned with the main axis of the flex container - so if your flex items are in a rowflex-basis specifies a width; if you’re using a column, flex-basis specifies a height. Flex basis will override width orheight, but won’t override max-width, max-height, min-width or min-height properties:
<p>If no <code>flex-basis</code> is specified,
flexbox uses the element width:</p>
<section>
<div style="width: 80%;">width: 80%</div>
<div style="width: 20%;">width: 20%</div>
</section>
<p>If <code>flex-basis</code> is specified,
it overrides the element width:</p>
<section>
<div style="width: 80%; flex-basis: 20%;">width: 80%; flex-basis: 20%;</div>
<div style="width: 20%; flex-basis: 80%;">width: 20%; flex-basis: 80%;</div>
</section>
<p><em>(I can't think of any reason you'd ever do this in real life,
but if you do, this is what happens!)
</em></p>
<p><code>flex-basis</code> can be in relative or absolute units, and
if the total size of the elements is smaller than the flex container,
it has the same effect as specifying width or height:</p>
<section>
<div style="flex-basis: 30%;">flex-basis: 30%</div>
<div style="flex-basis: 10vw;">10vw</div>
<div style="flex-basis: 5rem;">5rem</div>
<div style="flex-basis: 100px;">100px</div>
</section>
<p>The effect of <code>flex-basis</code>
is easiest to see when the elements add up to
more than 100% of the container size:</p>
<section>
<div style="flex-basis: 10%;">10%</div>
<div style="flex-basis: 30%;">30%</div>
<div style="flex-basis: 50%;">50%</div>
<div style="flex-basis: 70%;">70%</div>
</section>
<section>
<div style="flex-basis: 200px;">200px</div>
<div style="flex-basis: 400px;">400px</div>
<div style="flex-basis: 600px;">600px</div>
<div style="flex-basis: 800px;">800px</div>
</section>
<section>
<div style="flex-basis: 200%;">200%</div>
<div style="flex-basis: 200px;">200px</div>
<div style="flex-basis: 10px; background-color: crimson;">10px</div>
<div style="flex-basis: 20rem;">20rem</div>
</section>
<section>
<div style="height: 80%;">height: 80%;</div>
<div style="height: 20%;">height: 20%;</div>
</section>
<section>
<div style="height: 80%; flex-basis: 20%;">height: 80%;<br>flex-basis: 20%;</div>
<div style="height: 20%; flex-basis: 80%;">height: 20%;<br>flex-basis: 80%;</div>
</section>
<section>
<div style="flex-basis: 30%;">flex-basis: 30%;</div>
<div style="flex-basis: 10vw;">flex-basis: 10vw;</div>
<div style="flex-basis: 5rem;">flex-basis: 5rem;</div>
<div style="flex-basis: 100px;">flex-basis: 100px;</div>
</section>
<section>
<div style="flex-basis: 10%;">flex-basis: 10%;</div>
<div style="flex-basis: 30%;">flex-basis: 30%;</div>
<div style="flex-basis: 50%;">flex-basis: 50%;</div>
<div style="flex-basis: 70%;">flex-basis: 70%;</div>
</section>
<section>
<div style="flex-basis: 200px;">flex-basis: 200px;</div>
<div style="flex-basis: 400px;">flex-basis: 400px;</div>
<div style="flex-basis: 600px;">flex-basis: 600px;</div>
<div style="flex-basis: 800px;">flex-basis: 800px;</div>
</section>
<section>
<div style="flex-basis: 200%;">flex-basis: 200%;</div>
<div style="flex-basis: 200px;">flex-basis: 200px;</div>
<div style="flex-basis: 10px; background-color: crimson;">flex-basis: 10px;</div>
<div style="flex-basis: 20rem;">flex-basis: 20rem;</div>
</section>Next up, let’s meet flex-grow, and its counterpart flex-shrink. These both accept a unitless value, a non-negative integer value which controls how much that item will grow or shrink, proportional to the other items in the same flex container.
Let’s walk through that slowly.
Say we have a flex container here, that’s 730 pixels wide, with 5px of padding and a 5px flex gap. There are five <div> elements in the container, with their flex-basis set to 50px. Initially, they don’t have a flex-grow property - or rather, they have the default flex-grow value, which is zero - so they only take up as much space as their content, and the container has a bunch of empty space at the end:

Now, let’s assign the flex-grow property for each of our flex items - we’ll set them to 1, 2, 3, 4, and 5, respectively:

The flexbox layout engine is going to add those flex-grow values together — 1 + 2 + 3 + 4 + 5 = 15 — and then divide the available free space into 15 equal parts:

Then — this is the clever part — it’ll redistribute that space between the flex items based on their flex-grow values:

and then increase the size of each item to occupy the space allocated to it by that distribution:

flex-shrink is… more complicated.
The simple explanation is that a flex item with flex-shrink: 2 will shrink twice as fast as an element with flex-shrink: 1, and an element with flex-shrink: 0 will never shrink.
section {
* {
box-sizing: border-box;
}
width: 360px;
background-color: royalblue;
display: flex;
gap: 5px;
padding: 5px;
margin: 5px;
div {
flex-basis: 100px;
font-family: Consolas, 'Courier New', Courier, monospace;
font-size: 0.8rem;
background-color: gold;
padding: 5px;
}
}
<section>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
</section>
<section>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
<div>1</div>
</section>
<section>
<div>1</div>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
<div style="flex-shrink: 0;">0</div>
</section>
<section>
<div style="flex-shrink: 1;">1</div>
<div style="flex-shrink: 2;">2</div>
<div style="flex-shrink: 3;">3</div>
<div style="flex-shrink: 4;">4</div>
<div style="flex-shrink: 5;">5</div>
</section>
<section>
<div style="flex-shrink: 1;">1</div>
<div style="flex-shrink: 1;">1</div>
<div style="flex-shrink: 1;">1</div>
<div style="flex-shrink: 10;">10</div>
<div style="flex-shrink: 100;">100</div>
</section>If you’re interested, the mathematics behind it works like this:
- Calculate the overflow - how much space do we need to lose? If we have 4 x 200px elements in a 500px container, we’ve got 800px of content in a 500px container, so the overflow is 300px. You’ll also sometimes see this called the negative free space.
- Calculate the shrink factor for each element: this is
flex-basis(orwidth, if no flex basis is specified), multiplied by theflex-shrink - Determine each item’s share of the total shrink factor
- Reduce each item’s width in proportion to its share of the shrink factor.
Using the flex shorthand property
Although you can specify flex-basis, flex-grow and flex-shrink separately, it’s usually much easier to use the flex shorthand property:
.item {
flex: <grow> <shrink> <basis>;
}
Every item in a flexbox container has a default flex: 0 1 auto — i.e. don’t grow, shrink equally with other elements as necessary, and set initial width based on the element’s width.
If you use the flex property and only specify the grow value, the browser will set the flex-basis to 0%; this might seem counterintuitive but what it’s actually saying is “grow & shrink this item as necessary; don’t worry what size it was supposed to be”
* { box-sizing: border-box; }
section {
display: flex;
background-color: royalblue;
gap: 5px;
padding: 5px;
margin: 5px;
div {
background-color: gold;
padding: 5px;
}
&#b {
div {
flex: 1;
}
}
}
<section id="a">
<div>A</div>
<div>A</div>
<div>A</div>
<div>A</div>
</section>
<section id="b">
<div>B</div>
<div>B</div>
<div>B</div>
<div>B</div>
</section>
<section>
<div style="flex: 2;">C</div>
<div style="flex: 3;">C</div>
<div style="flex: 4;">C</div>
<div style="flex: 5;">C</div>
</section>Parting Thoughts about Flexbox
As you’ve seen in this section, flexbox gives us a whole new way to think about linear layouts in CSS. It’s incredibly flexible - hence the name - but that flexibility has a cost; at a certain point, it becomes almost impossible to look at a given combination of properties and values and predict how it’s going to behave.
Thing is: I don’t think it matters. Working with flexbox, for me, is as much about trial and error as it is about precision engineering; I’ll usually sketch out how I want something to appear at various device sizes, figure out how to create a flex layout that’s broadly correct, and then play around with specific properties and values to smooth out the rough edges.
The other thing to remember about flexbox: it’s just as valid for laying out a tiny component, like a button or a form field, as it is for laying out entire pages and sections. We’ll look at some examples of that in the next section of the course.
Review & Recap
In this section, we learned:
- Flexbox is part of the CSS Flexible Box Layout Module, finalised around 2012 and widely supported by 2017.
- It introduces a new layout model based on a main axis and a cross axis, activated with
display: flexordisplay: inline-flex. - Flexbox is one-dimensional: it arranges elements in a row or a column, unlike CSS Grid which handles two dimensions.
- We can control wrapping with
flex-wrapor shorthandflex-flow: <direction> <wrap>. - Gaps between items can be managed with
row-gap,column-gap, orgap, while container spacing still requirespaddingormargin. - Layout alignment uses
justify-content(main axis),align-items(cross axis), andalign-content(for multiple rows/columns). - Flexbox makes it easy to centre elements horizontally and vertically—solving a long-standing CSS challenge.
- Individual items can override container rules using
order(reordering),align-self, and baseline alignment. - Sizing and distribution of space is handled with
flex-grow,flex-shrink, andflex-basis, which determine how items expand or contract. - The shorthand
flex: <grow> <shrink> <basis>simplifies item sizing, with a default offlex: 0 1 auto.
References
- “Learn Flexbox with 30 Code Tidbits” by Samantha Ming
- A wonderful mini-course all about CSS flexbox, delivered as 30 short coding exercises: https://www.samanthaming.com/flexbox30/
- “CSS Flexbox Layout Guide” by Chris Coyler at css-tricks.com
- https://css-tricks.com/snippets/css/a-guide-to-flexbox/ - along with a handy high-res printable “quick reference” poster covering all the various flexbox properties and values
- “CSS Flexible Box Layout” at MDN
- MDN’s comprehensive documentation on the flexible box layout module and all the associated properties: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout