Flexbox

Table of Contents

  1. The Layout Problem
  2. Flex Container and Flex Items
  3. The Main Axis and the Cross Axis
  4. Spacing and Alignment
  5. gap
  6. Wrapping
  7. Sizing Items: grow, shrink, basis
  8. Recipe: A Navbar
  9. Recipe: Centering and Cards
  10. Try It Yourself

1. The Layout Problem

So far, every element has stacked from top to bottom, because that's how block elements flow. Real pages need more: a logo on the left and a menu on the right, three cards in a row, a button centered in a box.

For years this was done with awkward tricks — floats, tables, fixed widths. Flexbox ("flexible box") is the modern tool built for the job. It's designed to lay items out along one line — a row or a column — and to distribute the space between them.

2. Flex Container and Flex Items

Flexbox starts with one declaration on a parent:

<div class="row">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
.row {
  display: flex;
}

The parent becomes a flex container, and its direct children become flex items. Immediately, the three divs sit side by side instead of stacking. Only the direct children are affected; grandchildren aren't.

Some properties go on the container (how to arrange the items), and some on the items (how each one behaves). Knowing which is which is half the battle.

3. The Main Axis and the Cross Axis

Flexbox thinks in two directions:

flex-direction: row (default)

 main axis  ───────────────────────────►
 ┌─────┐ ┌─────┐ ┌─────┐
 │  1  │ │  2  │ │  3  │    cross axis
 └─────┘ └─────┘ └─────┘         │
                                  ▼

flex-direction: column

 main axis  │   ┌─────┐
            │   │  1  │
            │   └─────┘     cross axis
            │   ┌─────┐   ─────────────►
            ▼   │  2  │
                └─────┘
  • The main axis runs in the direction items are laid out.
  • The cross axis is perpendicular to it.

flex-direction chooses the main axis: row (the default, left to right), column (top to bottom), or the reversed versions row-reverse and column-reverse. Everything in the next section refers to main and cross, not to horizontal or vertical — so the same property works in either direction.

Reversing changes only the visual order, not the order for screen readers or keyboard users. That can confuse them, so use it sparingly.

4. Spacing and Alignment

Two container properties do most of the work:

PropertyAxisControls
justify-contentMainHow items are spread along the line
align-itemsCrossHow items line up across the line

justify-content values:

ValueResult
flex-startItems packed at the start (default)
flex-endPacked at the end
centerPacked in the middle
space-betweenFirst and last at the edges, equal space between
space-aroundEqual space around each item
space-evenlyExactly equal space everywhere

align-items values: stretch (default: items fill the cross size), flex-start, flex-end, center, and baseline (text baselines line up).

.row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

A mental shortcut: "justify" is along the main axis, "align" is across it. If you're unsure, try both and look at DevTools — in the Elements panel, a small flex badge next to the element toggles an overlay that draws the axes and free space.

5. gap

To put space between items, use gap on the container instead of margins on each child:

.row {
  display: flex;
  gap: 1rem;
}

gap adds space only between items, never on the outside edges, so there is no leftover margin on the last item to fight with. You can give two values: gap: 1rem 2rem is row gap then column gap.

6. Wrapping

By default, a flex container keeps everything on one line, shrinking items to fit — even until they overflow. To let items flow onto a new line, turn on wrapping:

.row {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

Items now keep their size and wrap onto the next row when there isn't room. This is the basis of many "responsive without media queries" layouts. The shorthand flex-flow: row wrap sets direction and wrap together.

7. Sizing Items: grow, shrink, basis

Properties on the items control how they share the container's free space:

PropertyMeaning
flex-basisThe item's starting size on the main axis, before sharing
flex-growHow much of the leftover space it gets (0 = none)
flex-shrinkHow much it gives up when there isn't enough space

The shorthand is flex: grow shrink basis. The values you'll use most:

.item { flex: 1; }          /* share the free space equally */
.sidebar { flex: 0 0 16rem; }   /* fixed 16rem, never grows or shrinks */
.content { flex: 1; }       /* take everything that's left */

flex: 1 on every item gives you equal-width columns. If one item has flex: 2 and the other two flex: 1, the big one gets double the share.

margin: auto is a handy trick: on a flex item, margin-left: auto pushes it all the way to the right, absorbing all the free space.

8. Recipe: A Navbar

Here's a header with the name on the left and the links on the right:

<header class="site-header">
  <a href="index.html" class="brand">Dara Sok</a>
  <nav aria-label="Main">
    <ul class="nav-list">
      <li><a href="index.html">Home</a></li>
      <li><a href="about.html">About</a></li>
      <li><a href="#contact">Contact</a></li>
    </ul>
  </nav>
</header>
.site-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 1rem;
}

.nav-list {
  display: flex;
  gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

Two flex containers are used: the header spreads the brand and the nav apart, and .nav-list lays the <li>s in a row. Nesting flex containers like this is normal — each handles one line of the design.

9. Recipe: Centering and Cards

Centering. Vertically centering something was once famously hard. With Flexbox, it's three lines:

.centered {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 50vh;
}

A row of cards that share space, wrap when needed, and have matching widths:

.projects {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.projects .card {
  flex: 1 1 14rem;
}

flex: 1 1 14rem reads: "start at 14rem, grow to fill spare space, and shrink if you must." On a wide screen you might get three cards in a row. On a narrow one they wrap to two, then one, with no media query.

A caveat: in a wrapping flex layout, each row shares space on its own, so the last row's cards can end up stretched wider than the others. Chapter 10's Grid doesn't have this problem, which is one reason it's better for card galleries.

10. Try It Yourself

  1. See the axes. Make a .row with three boxes and turn on Flexbox. Change flex-direction to column, then play with justify-content and align-items. Use the DevTools flex overlay.
  2. Build the navbar. Use the recipe in section 8 on both pages. Check that the links are reachable with Tab.
  3. Make a sticky footer. Give body display: flex, flex-direction: column, and min-height: 100vh, then main flex: 1. Now the footer sits at the bottom even on short pages.
  4. Lay out the projects. Make your project <article>s flex with flex: 1 1 14rem. Resize the window and watch them wrap. Add a fourth card and see what the last row does.
  5. Push with margin. In a navbar, add a "Sign in" link and give it margin-left: auto. Where does it go?
  6. Reorder carefully. Use flex-direction: row-reverse on the nav, then Tab through it. What do you notice about the order?

Next, Grid: the other half of modern layout, built for rows and columns at the same time.

Buy Me a Coffee

Found this tutorial helpful? Consider buying me a coffee to support more free content!