📐 Lesson 13: Layout with Flexbox
So far, every element on your page stacks top-to-bottom like a pile of bricks. That's fine for simple pages — but what about a navigation bar with links side by side? A sidebar next to your main content? A row of cards? Flexbox is the CSS layout system that makes all of that effortless.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain the relationship between a flex container and its flex items
- Use
display: flexto lay out elements in a row or column - Control direction with
flex-direction - Distribute space with
justify-content - Align items with
align-items - Wrap items onto multiple lines with
flex-wrap - Control how items grow and shrink with
flex-grow,flex-shrink, andflex-basis - Build a navigation bar and a card layout using Flexbox
Estimated Time: 60 minutes
Hands-on: You'll build a navigation bar and a responsive card row for your site.
📑 In This Lesson
The Problem Flexbox Solves
By default, block elements stack vertically. Each one takes the full width and sits below the previous one. Before Flexbox, developers used float and absolute positioning to place elements side by side — hacks that were fragile, hard to maintain, and terrible with spacing.
Flexbox was designed from scratch to solve one-dimensional layout — arranging items in a row or in a column with precise control over alignment, spacing, and sizing. It answers questions like:
- "How do I put three cards side by side?"
- "How do I center something both horizontally and vertically?"
- "How do I push the last nav link to the right?"
- "How do I make items wrap to the next line when they run out of space?"
One line of CSS — display: flex — unlocks all of this.
Flex Container & Flex Items
Flexbox has two roles: the container (parent) and its items (direct children). You apply display: flex to the parent, and all of its direct children automatically become flex items.
<div class="card-row"> <!-- flex container -->
<div class="card">A</div> <!-- flex item -->
<div class="card">B</div> <!-- flex item -->
<div class="card">C</div> <!-- flex item -->
</div>
.card-row {
display: flex;
}
That's it. The three cards now sit side by side in a row instead of stacking vertically. The container controls the layout; the items respond.
💡 Key Insight
Flex properties are split between the container and the items. Properties like justify-content, align-items, flex-direction, flex-wrap, and gap go on the container. Properties like flex-grow, flex-shrink, flex-basis, and align-self go on individual items.
Flex Direction — Row or Column
flex-direction controls which way items flow. It defines the main axis — the primary direction of layout.
/* Default — items flow left to right */
.container { flex-direction: row; }
/* Items flow top to bottom (like default block behavior, but with flex powers) */
.container { flex-direction: column; }
/* Reversed versions */
.container { flex-direction: row-reverse; } /* right to left */
.container { flex-direction: column-reverse; } /* bottom to top */
| Value | Main Axis | Items Flow | Common Use |
|---|---|---|---|
row |
Horizontal → | Left to right | Navigation bars, card rows |
column |
Vertical ↓ | Top to bottom | Sidebar stacks, mobile nav |
row-reverse |
Horizontal ← | Right to left | RTL layouts, reversed order |
column-reverse |
Vertical ↑ | Bottom to top | Chat messages (newest at bottom) |
Main Axis vs. Cross Axis
Understanding Flexbox requires knowing two axes:
- Main axis — the direction items flow (set by
flex-direction). Forrow, it's horizontal. Forcolumn, it's vertical. - Cross axis — the perpendicular direction. For
row, the cross axis is vertical. Forcolumn, it's horizontal.
(horizontal)"] CA["Cross Axis ↓
(vertical)"] end style MA fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style CA fill:#f0fdf4,stroke:#22c55e,stroke-width:2px,color:#1e293b
Why does this matter? Because justify-content works along the main axis, and align-items works along the cross axis. Switch the direction, and they swap which dimension they control.
justify-content — Distributing Space Along the Main Axis
justify-content controls how items are positioned and how extra space is distributed along the main axis.
.container {
display: flex;
justify-content: flex-start; /* default */
}
| Value | Behavior | Visual (row) |
|---|---|---|
flex-start |
Items packed to the start | [A][B][C] |
flex-end |
Items packed to the end | [A][B][C] |
center |
Items centered | [A][B][C] |
space-between |
First item at start, last at end, equal space between | [A] [B] [C] |
space-around |
Equal space around each item | [A] [B] [C] |
space-evenly |
Equal space between and at edges | [A] [B] [C] |
The three space-* values are the workhorses. space-between is probably the most used — it's perfect for navigation bars where you want the logo on the left and links on the right.
/* Navigation bar: logo left, links right */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 24px;
}
align-items — Cross-Axis Alignment
align-items controls alignment along the cross axis — the axis perpendicular to the main flow. For a row layout, that means vertical alignment.
.container {
display: flex;
align-items: stretch; /* default */
}
| Value | Behavior |
|---|---|
stretch |
Items stretch to fill the container's cross axis (default) |
flex-start |
Items align to the start of the cross axis (top for rows) |
flex-end |
Items align to the end of the cross axis (bottom for rows) |
center |
Items centered along the cross axis |
baseline |
Items align by their text baselines |
The Holy Grail: Perfect Centering
Before Flexbox, centering something both horizontally and vertically was surprisingly difficult. With Flexbox, it's two lines:
/* Perfect centering — both axes */
.centered {
display: flex;
justify-content: center; /* center on main axis (horizontal) */
align-items: center; /* center on cross axis (vertical) */
height: 100vh; /* full viewport height so there's space to center in */
}
This is one of the most celebrated improvements Flexbox brought to CSS. Developers used to need complex workarounds for this; now it's trivial.
align-self — Override for One Item
If most items should align one way but a single item needs different alignment, use align-self on that item:
.container {
display: flex;
align-items: center; /* most items centered */
}
.special-item {
align-self: flex-end; /* this one sticks to the bottom */
}
flex-wrap — Wrapping to New Lines
By default, flex items try to fit on one line — they'll shrink to avoid overflowing. If you want items to wrap onto new lines (like a grid of cards), use flex-wrap:
.card-grid {
display: flex;
flex-wrap: wrap; /* allow items to wrap */
}
.card-grid .card {
width: 300px; /* each card wants to be 300px */
}
Now the cards sit side by side, and when the container isn't wide enough to fit the next card, it wraps to a new line — like words in a paragraph.
| Value | Behavior |
|---|---|
nowrap |
All items on one line (default) — they shrink if needed |
wrap |
Items wrap to new lines when they run out of space |
wrap-reverse |
Items wrap, but new lines appear above instead of below |
💡 Shorthand: flex-flow
flex-flow combines flex-direction and flex-wrap into one property:
/* flex-flow: direction wrap; */
.container {
flex-flow: row wrap;
}
/* Same as:
flex-direction: row;
flex-wrap: wrap;
*/
gap — Spacing Between Items
Before gap, developers added margin to flex items and had to deal with extra margin on the first or last item. The gap property creates space between items without affecting the outer edges.
.card-row {
display: flex;
gap: 20px; /* 20px between each item */
}
/* You can also set row and column gaps separately */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px 16px; /* 20px between rows, 16px between columns */
}
gap is cleaner than using margins because:
- No extra space on the outer edges
- No need for
:first-childor:last-childoverrides - It works with wrapping — gap applies between wrapped rows too
You can also use row-gap and column-gap individually if you need different spacing in each direction.
flex-grow, flex-shrink, flex-basis
These three properties go on flex items (not the container) and control how items share available space.
flex-basis — Starting Size
flex-basis sets the item's ideal starting size before any growing or shrinking. It works like width in a row layout (or height in a column layout).
.card {
flex-basis: 300px; /* start at 300px, then grow or shrink from there */
}
flex-grow — How Much Extra Space to Take
flex-grow controls how much of the leftover space an item absorbs. The default is 0 — items don't grow.
/* All items share extra space equally */
.item {
flex-grow: 1;
}
/* The middle item gets twice as much extra space */
.sidebar { flex-grow: 1; } /* 1 share */
.main { flex-grow: 2; } /* 2 shares */
.sidebar2 { flex-grow: 1; } /* 1 share */
In the example above, if there's 400px of extra space, .sidebar gets 100px, .main gets 200px, and .sidebar2 gets 100px.
flex-shrink — How Much to Give Up
flex-shrink is the opposite of flex-grow — it controls how much an item shrinks when there isn't enough space. The default is 1 — all items shrink equally.
/* This item refuses to shrink */
.logo {
flex-shrink: 0;
}
/* This item shrinks twice as fast as others */
.description {
flex-shrink: 2;
}
The flex Shorthand
The flex shorthand combines all three in one line:
/* flex: grow shrink basis; */
.item { flex: 0 1 auto; } /* default: don't grow, shrink equally, auto sizing */
.item { flex: 1; } /* grow equally, shrink equally, basis 0 */
.item { flex: 1 1 300px; } /* grow equally, shrink equally, start at 300px */
.item { flex: 0 0 250px; } /* don't grow, don't shrink, fixed at 250px */
The most common pattern you'll use: flex: 1 to make items share space equally, or flex: 0 0 250px for a fixed-size item.
Takes 1 share
= 300px"] A --> C["flex: 2
Takes 2 shares
= 600px"] style A fill:#f8fafc,stroke:#64748b,stroke-width:2px,color:#1e293b style B fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style C fill:#eff6ff,stroke:#6366f1,stroke-width:2px,color:#1e293b
Common Flexbox Patterns
Here are the patterns you'll use over and over again. Copy these as starting points for your own layouts.
Pattern 1: Navigation Bar
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 24px;
background-color: #1e293b;
color: white;
}
.navbar .nav-links {
display: flex;
gap: 24px;
list-style: none;
}
<nav class="navbar">
<a href="/" class="logo">MySite</a>
<ul class="nav-links">
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
Pattern 2: Card Row (Responsive)
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.card-grid .card {
flex: 1 1 280px; /* grow, shrink, min 280px before wrapping */
padding: 20px;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
The magic of flex: 1 1 280px: each card wants to be at least 280px. If there's room for three cards, you get three columns. If the screen is narrower, cards wrap — two per row, then one. No media queries needed.
Pattern 3: Sidebar Layout
.page-layout {
display: flex;
gap: 30px;
}
.sidebar {
flex: 0 0 250px; /* fixed 250px, won't grow or shrink */
}
.main-content {
flex: 1; /* takes all remaining space */
}
<div class="page-layout">
<aside class="sidebar">
<!-- navigation or filters -->
</aside>
<main class="main-content">
<!-- primary content -->
</main>
</div>
Pattern 4: Footer with Sections
.footer-content {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 30px;
padding: 40px 24px;
}
.footer-section {
flex: 1 1 200px;
}
Pattern 5: Centering Anything
.hero {
display: flex;
justify-content: center;
align-items: center;
min-height: 60vh;
text-align: center;
}
Hands-on Exercise
🏋️ Exercise: Flexbox Navigation & Card Row
Objective: Convert your navigation to a Flexbox layout and create a row of cards on your homepage.
Part A — Flex Navigation Bar:
- In your
css/style.css, adddisplay: flexto yourheaderor<nav>element - Use
justify-content: space-between;so the site name sits left and the nav links sit right - Use
align-items: center;to vertically center everything - Make the nav link list itself a flex container with
gap: 20px - Remove the default list bullets with
list-style: none;and resetpadding: 0;andmargin: 0;
Part B — Card Row:
- On your homepage, create a section with 3 or more cards inside a container
<div> - Give the container
display: flex; flex-wrap: wrap; gap: 20px; - Give each card
flex: 1 1 250px; - Resize your browser window and watch the cards responsively reflow
💡 Hint
For the navigation, the key structure is: a <nav> with display: flex; justify-content: space-between; align-items: center; containing the site name and a <ul>. The <ul> also gets display: flex; gap: 20px;. Nested flex containers are perfectly normal — you'll use them all the time.
✅ Example Solution
/* ===========================
Navigation Bar (Flex)
=========================== */
.site-nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 24px;
background-color: var(--color-primary, #1e293b);
color: white;
}
.site-nav a {
color: white;
text-decoration: none;
}
.site-nav a:hover {
text-decoration: underline;
}
.site-nav .logo {
font-size: 1.25rem;
font-weight: 700;
}
.site-nav .nav-links {
display: flex;
gap: 20px;
list-style: none;
margin: 0;
padding: 0;
}
/* ===========================
Card Row (Flex)
=========================== */
.card-row {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin-top: 24px;
}
.card-row .card {
flex: 1 1 250px;
background-color: white;
padding: 20px 24px;
border: 1px solid var(--color-border, #e2e8f0);
border-radius: 8px;
}
.card-row .card h3 {
margin-top: 0;
}
.card-row .card p {
color: var(--color-text-light, #64748b);
}
<!-- Updated navigation -->
<nav class="site-nav">
<a href="index.html" class="logo">My Website</a>
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="recipe.html">Recipes</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
<!-- Card row on homepage -->
<section>
<div class="container">
<h2>What You'll Find Here</h2>
<div class="card-row">
<div class="card">
<h3>🍳 Recipes</h3>
<p>My favorite home-cooked meals, from quick weeknight dinners to weekend projects.</p>
<a href="recipe.html">Browse recipes →</a>
</div>
<div class="card">
<h3>📖 About Me</h3>
<p>A little about who I am, what I do, and why I built this site.</p>
<a href="about.html">Learn more →</a>
</div>
<div class="card">
<h3>✉️ Get in Touch</h3>
<p>Have a question or just want to say hello? Drop me a message.</p>
<a href="contact.html">Contact me →</a>
</div>
</div>
</div>
</section>
🎯 Quick Quiz
Question 1: What does display: flex do to an element's direct children?
Question 2: Which property distributes space between flex items along the main axis?
Question 3: How do you perfectly center an element both horizontally and vertically with Flexbox?
Question 4: What does flex-wrap: wrap do?
Question 5: What does flex: 1 1 300px mean?
Summary
🎉 Key Takeaways
- Flexbox is a one-dimensional layout system — arrange items in a row or a column
- Apply
display: flexto the parent — its direct children become flex items flex-directionsets the main axis:row(default) orcolumnjustify-contentdistributes space along the main axis (e.g.,space-between,center)align-itemsaligns items on the cross axis (e.g.,center,stretch)flex-wrap: wraplets items flow onto new lines — essential for responsive card layoutsgapcreates space between items without affecting outer edges — cleaner than marginsflex: grow shrink basiscontrols how items share space —flex: 1is the most common shorthand- Perfect centering:
justify-content: center; align-items: center;— the Flexbox gift to CSS - Common patterns: navigation bars (
space-between), card grids (wrap+flex-basis), sidebars (flex: 0 0 fixed+flex: 1)
📁 Your Project So Far
my-website/
├── css/
│ └── style.css ← now with Flexbox nav and card layout
├── images/
│ └── sunset.jpg
├── index.html ← flex nav bar + card row
├── about.html
├── recipe.html
└── contact.html
🚀 What's Next?
Flexbox handles one-dimensional layouts beautifully — rows or columns. But what about two-dimensional layouts where you need to control rows and columns at the same time? Think photo galleries, page-level layouts, and dashboard grids. In the next lesson, you'll learn CSS Grid — Flexbox's two-dimensional sibling that gives you full grid-based layout control.