Flexbox: laying out in one dimension
So far, your elements stack one above the other, as in a document. Real websites place things side by side: a logo on the left and a menu on the right, cards in a row, a centered title. Flexbox is the tool designed for exactly this. In this lesson you will learn it step by step and use it to build your portfolio’s header and navigation.
The problem Flexbox solves
Block elements take a full line, so a list of menu links stacks vertically. Before Flexbox, developers used awkward tricks (floats, tables, fixed widths) to line things up. Flexbox, short for “flexible box layout”, makes it easy to align, space and distribute items in a row or a column, even when their sizes are unknown.
Container and items
Flexbox always involves two roles:
- The flex container: the parent, on which you write
display: flex. - The flex items: its direct children, which are laid out automatically.
<ul class="menu">
<li><a href="#projects">Projects</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
.menu {
display: flex;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
}
With a single declaration, the three items now sit on one line. The gap property adds space between them, with no need for margins on each item. Only the direct children become flex items: a link nested deeper inside is not affected.
The two axes
Flexbox thinks in axes. The main axis is the direction in which items flow, and the cross axis is perpendicular to it.
.container {
display: flex;
flex-direction: row; /* default: items flow left to right */
}
.container.vertical {
flex-direction: column; /* items flow top to bottom */
}
When you switch to column, the axes swap too: the main axis is now vertical. This is the key to understanding Flexbox alignment.
Aligning along the main axis: justify-content
justify-content distributes items along the main axis.
| Value | Effect |
|---|---|
flex-start | Items at the start (default) |
center | Items grouped in the center |
flex-end | Items at the end |
space-between | First item at the start, last at the end, equal space between |
space-around | Equal space around each item |
space-evenly | Equal space between and around items |
Aligning along the cross axis: align-items
align-items controls the cross axis, for example the vertical position of items in a row.
stretch(default): items fill the container’s height.center: items centered vertically.flex-startandflex-end: aligned to the start or end.baseline: aligned on the text baseline.
Centering something both ways, long considered tricky in CSS, is now trivial:
.center-me {
display: flex;
justify-content: center;
align-items: center;
min-height: 12rem;
}
Building the portfolio header
Here is a typical header: the site name on the left, the menu on the right, vertically centered.
<header class="site-header">
<a class="logo" href="index.html">Maya Carter</a>
<nav aria-label="Main">
<ul class="menu">
<li><a href="#projects">Projects</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</header>
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 1.5rem;
background-color: #ffffff;
border-bottom: 1px solid #e2e8f0;
}
.logo {
font-weight: 700;
text-decoration: none;
color: #134e4a;
}
The header is a flex container with two children, the logo and the nav. space-between pushes them to opposite ends. The menu itself is another flex container, so Flexbox containers nest naturally.
Wrapping
By default, items stay on a single line and may shrink to fit. To let them flow onto several lines when there is not enough room, enable wrapping:
.tags {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
Wrapping is ideal for lists of tags, buttons or small cards. With wrap, row and column gaps can be set separately with row-gap and column-gap.
Controlling the items: flex-grow, flex-shrink and flex-basis
You can also configure individual items. Three properties define how an item uses the available space, usually written in a shorthand called flex.
flex-basis: the ideal starting size of the item along the main axis.flex-grow: how much the item may grow to take free space, as a share relative to the others.flex-shrink: how much it may shrink when space runs short.
.item { flex: 1; } /* equal-sized, growing to share the space */
.sidebar { flex: 0 0 15rem; } /* fixed 15rem: no grow, no shrink */
.content { flex: 1; } /* takes whatever remains */
A two-column layout is then simple: a fixed-width sidebar and a main area that fills the rest. Use align-self on a single item to override the container’s align-items for that item, and order to change the visual order. Be careful with order: it changes only what is seen, not the reading order for keyboard and screen reader users.
A flexible row of cards
.cards {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.cards > article {
flex: 1 1 15rem; /* grow, shrink, start at 15rem */
}
Each card starts at 15rem, grows to fill leftover space, and wraps to a new line when the row is full. This produces a layout that adapts to the window width with no media query. If one line holds fewer items, the cards on that line stretch larger, which may or may not be what you want. For strict columns, CSS Grid, the subject of the next lesson, is better.
Flexbox or something else?
Flexbox is designed for one dimension: a row or a column. It excels at components such as menus, toolbars, form rows, media objects (image beside text) and small groups of cards. For layouts that must control rows and columns at the same time, use Grid, which we will see next. The two work very well together.
Tip. The developer tools help here as well: browsers display a small “flex” badge next to flex containers in the Elements panel, and can overlay the layout so that you see the items and gaps.
Warning. display: flex only affects the direct children of the container. If nothing moves, check that your elements are direct children, and that you applied the property to the parent, not to the items.
Your turn
- Turn the header of your portfolio into a flex container with the logo on the left and the menu on the right, aligned vertically.
- Make the menu horizontal with a gap between links.
- Make a row of three project cards that wraps on narrow windows, with each card starting at about 16rem.
- Resize the browser window and watch the wrapping behavior.
- Challenge: center a call-to-action button both horizontally and vertically in a box 12rem high.
See a solution
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.menu {
display: flex;
gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
}
.cards {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.cards > article {
flex: 1 1 16rem;
}
.cta-box {
display: flex;
justify-content: center;
align-items: center;
min-height: 12rem;
}
Remember to wrap your three article elements inside a parent with the class cards: the container must be their direct parent.
Key takeaways
display: flexon a parent turns its direct children into flex items laid out in a row by default.justify-contentaligns along the main axis,align-itemsalong the cross axis;flex-direction: columnswaps the axes.- Use
gapfor spacing andflex-wrap: wrapto let items flow onto several lines. flex: 1 1 15remcreates items that grow, shrink and wrap gracefully.- Flexbox is for one-dimensional layout: menus, headers, rows of components.