Flexbox, short for flexible boxes—which folks will often just shorten all the way to flex—is a later (mid-2010s, depending on how you count) addition to CSS. Flex was created to facilitate and allow CSS layouts that the box model (with its floats and position) either made difficult, brittle, or even impossible. It is a display property.
And let me tell you—being a web designer was a whole lot harder before flex came on the scene. (Hence the “Finally.”) Notice that we haven’t yet talked about vertical centering, for instance—you don’t want to know. And you don’t have to worry about it! Flex encapsulates a lot of decent design paradigms in its system.
Main and cross axes
Flexbox is a one-dimensional layout system—meaning it is (usually) focused on arranging items either horizontally in rows, or vertically in columns. These are called the axes, and the one running in the direction of your flex items is your main axis. Perpendicular to this is your cross axis:
Flex also lets us position elements along the axes, in both directions—in relation to the start or the end of the direction. For the main axis, you justify; for the cross axis, you align:
For rows: the default, we justify left/right, align top/bottom.
For columns: perpendicular, we justify top/bottom, align left/right.
Like a lot of CSS, flex has shorthand properties. But we would avoid them—the system is hard enough to understand, and we aren’t being charged by the CSS line. This will be true when we get to
grid as well—often being a little bit more verbose in your code will make things easier to understand, especially starting out.
Container (parent) properties
Unlike most (…all?) of the CSS we’ve been introduced to, flex is applied on a parent element—but actually adjusts the layout of the children. An element with
display: flex; is really telling you what its kids are going to be doing.
There is also
display: inline-flex; which behaves the same, but the parent behaves as an inline element while its children are flexing.
After specifying an element as flex, we can set its main axis with flex-direction. By default (you don’t have to write it) this behaves as
flex-direction: row;, so you’ll generally only be adding it when you want something going vertical—with
flex-direction: column; :
You can also combine these with a reverse option, which visually reorders the items along the main axis, flipping the start and end:
Keep in mind that all flex reordering is only visual—it does not change the order in the DOM. This means that keyboard navigation and screen readers still sequence through the items as they are in the HTML. So for good accessibility, keep in mind that semantic order!
Since flexbox is one-dimensional, by default it is going to try and cram everything into one
flex-wrap: wrap; property (which behaves as
flex-wrap: nowrap; by default):
You can use this to make grids, and it is often sufficient. But but the more recent CSS grid properties will give you more control! We’ll talk about
There is also a reverse option here, which will wrap items from end to start:
Okay, so most of what we’ve seen here is somewhat possible using floats and positioning—though not at all easily and only when you know the size/count of your content.
But justify-content is where flexbox starts to allow novel layouts, by dividing up the extra/available free space elements—akin to distribute options in Figma/
justify-content does this on our main axis:
When our main axis is vertical, with
flex-direction: column; :
And then perpendicular to justify along the main axis, flexbox has align-items to position elements along the cross axis. It has similar values:
When we have a flex element with
flex-wrap set, we can also position the lines within the parent/container—akin to
justify-content with each line:
align-content can also be used with a vertical /
flex-direction: column; axis, not shown here. This doesn’t often come up, as you have to specify/know a height to force a column wrap.
While you could use margin to separate your flex children, it would apply to the items on the outer edges, too. (Hence all our
:not(:first-child) selectors, in examples.) Flex recently gained support for intuitive gap properties, which fix this problem—by applying spacing only between children.
This is particularly helpful with dynamic, wrapping content and responsive designs—where you won’t always know which element ends or starts a line (to take their margin off):
Note that the
gap properties are also shared (in name and behavior) with
display: grid;, when we get there!
Item (child) properties
Flexbox is usually applied on the parent/container. But once you’ve set
display: flex; on an element, there are also some individual override properties that can be given to its children, flex items.
Kind of like the reverse properties—you can individually apply an order to a flex item (child). Items with the same/tied order (like everything with the default of
order: 0;) will be displayed in their DOM/source order:
Other order selectors (like
:first-child) won’t be fooled by this reordering—as you can see, we used them here. They still use the DOM order. And again, this change is only visual—so don’t use it when screen reader/content sequence accessibility is a concern!
These tell the child items to… grow or shrink, if necessary—defining the amount of available/remaining space in the container an element should take up. (I like to think of these as bento boxes.)
It takes a unitless proportional value (think fractions or a factor/multiplier). If you give one flexed child
flex-grow: 1; it will take up all the extra space; another element with
flex-grow: 2; would then take twice as much of that space as the first one (the available space with 3 total units):
flex-shrink works the same way—defining what proportion an element should shrink when forced to by the flex layout. The most use you’ll see of this is
flex-shrink: 0;, which tells all the other items to shrink instead!
Flex-basis is a little like width and height—depending on your main axis. This property defines what the child item’s content box size should be before any remaining space is distributed. This defaults to
auto, which falls back to any specified
height—and if those aren’t present, will just use the size of the content.
You specify this basis with normal length units like
Finally, we have an individual override for an align-items property set on the parent—which adjusts (with the same keywords/values) the alignment of the specific child item it is applied to:
This is a lot of stuff! Flex can be tough to wrap one’s head around, but it is so much better than float and width and margin shenanigans. Much of what you look at on the web is laid out in flex (and its sort-of successor which we keep hinting at, grid).