📐 Flexbox Fundamentals
For years, centering a box on a page was one of the hardest "easy" problems in CSS. Flexbox ended that struggle. It gives you a simple, predictable way to arrange items in a single direction — a row or a column — and to align, space, and resize them with a handful of properties. If you've ever fought with floats, this lesson is where the fighting stops.
Week 1 · Thursday: CSS Layout · Lecture 1
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain the flex container / flex item relationship and the roles of the main axis and cross axis
- Turn any element into a flex container with
display: flexand control flow withflex-direction - Distribute items along the main axis with
justify-contentand align them on the cross axis withalign-items - Let layouts reflow with
flex-wrapand thegapproperty - Size individual items with the
flexshorthand (flex-grow,flex-shrink,flex-basis) - Build real patterns: a nav bar, a responsive card row, and a sticky-footer page shell
Estimated Time: 60 minutes
Practice: Build a responsive navigation bar and a wrapping card gallery from scratch.
In This Lesson
Why Flexbox?
Flexbox — short for the Flexible Box Layout — is a CSS layout model built for arranging items in one dimension at a time: a horizontal row or a vertical column. Think of it like a smart shelf. You set the shelf to display: flex, drop items onto it, and the shelf figures out how to space them, whether to stretch them to equal height, and how to squeeze or grow them when the window resizes.
Before Flexbox, developers leaned on float and absolute positioning to fake layouts. Those techniques were brittle: vertical centering was a party trick, equal-height columns needed hacks, and a resized window could shatter the whole page. Flexbox replaced all of that with intent-based properties — you describe what you want ("space these evenly", "center this vertically") and the browser does the math.
Flexbox is the right tool whenever you're laying out content in a line: navigation bars, toolbars, button groups, form rows, and card strips. For full two-dimensional page grids you'll reach for CSS Grid — the very next lesson — and the two work beautifully together.
The Two Axes
Everything in Flexbox is defined relative to two axes. Get these straight and every property clicks into place.
- Main axis — the direction items are laid out along. By default this runs left-to-right.
justify-contentpositions items along it. - Cross axis — perpendicular to the main axis (top-to-bottom by default).
align-itemspositions items along it.
The crucial twist: when you change flex-direction to column, the axes rotate. The main axis becomes vertical and the cross axis becomes horizontal — so justify-content now works vertically. This is the single most common source of "why isn't this centering?" confusion, so keep the diagram below in mind.
row direction the main axis is horizontal. Switch to column and these two arrows swap orientation.| Term | Meaning |
|---|---|
| Flex container | The parent element you set to display: flex |
| Flex item | Each direct child of the container (grandchildren are not items) |
| Main axis | Direction items flow; controlled by flex-direction |
| Cross axis | The perpendicular axis |
Creating a Flex Container
Flexbox starts with a single declaration on the parent. Every direct child immediately becomes a flex item — no class needed on the children.
/* Turn a parent into a flex container */
.container {
display: flex;
}
/* inline-flex behaves like an inline element on the outside,
but lays out its children with flexbox on the inside */
.badge-row {
display: inline-flex;
}
Here is a complete, minimal example you can paste into a file and open in a browser:
<div class="flex-container">
<div class="flex-item">Item 1</div>
<div class="flex-item">Item 2</div>
<div class="flex-item">Item 3</div>
</div>
.flex-container {
display: flex;
gap: 10px; /* modern spacing — no margins needed */
border: 2px solid #334155;
padding: 10px;
}
.flex-item {
background: #e2e8f0;
padding: 20px;
}
By default the three items sit in a row, each only as wide as its content, left-aligned. From here, every other property is about redistributing and resizing those items.
Flex Direction
flex-direction sets which way the main axis points — the single most important choice, because it determines what every alignment property does.
.container {
flex-direction: row; /* default — items left to right */
flex-direction: row-reverse; /* items right to left */
flex-direction: column; /* items top to bottom */
flex-direction: column-reverse; /* items bottom to top */
}
📖 Direction rotates your mental model
When you set flex-direction: column, justify-content now controls vertical spacing and align-items controls horizontal alignment. The property names never change — only which axis they act on. This is by design and, once internalized, is a superpower.
Justify Content
justify-content distributes items along the main axis. If your items don't fill the container, this decides where the extra space goes.
.container {
justify-content: flex-start; /* default — pack items at the start */
justify-content: flex-end; /* pack items at the end */
justify-content: center; /* center the group */
justify-content: space-between; /* first & last hug the edges, gaps equal */
justify-content: space-around; /* equal space around each item */
justify-content: space-evenly; /* equal space between AND at the ends */
}
justify-content.A real-world nav bar is one line of Flexbox: push the logo to the left and the links to the right.
.nav {
display: flex;
justify-content: space-between; /* logo left, links right */
align-items: center; /* vertically centered */
padding: 1rem;
}
.nav-links {
display: flex;
gap: 1rem; /* even spacing between links */
}
Align Items & Align Self
align-items positions items on the cross axis. In a default row, that means vertically.
.container {
align-items: stretch; /* default — items stretch to fill cross axis */
align-items: flex-start; /* align to the top */
align-items: flex-end; /* align to the bottom */
align-items: center; /* vertically centered */
align-items: baseline; /* align items by their text baselines */
}
✅ The famous "center anything" recipe
Perfect centering — horizontal and vertical — is just three lines. This alone justifies learning Flexbox:
.hero {
display: flex;
justify-content: center; /* horizontal */
align-items: center; /* vertical */
min-height: 400px;
}
Need one item to break ranks? align-self overrides align-items for a single item.
.container { align-items: center; }
.special-item {
align-self: flex-end; /* just this one drops to the bottom */
}
A classic use of align-items: center is a media card — an avatar next to a block of text, both vertically centered no matter the text length:
.card {
display: flex;
align-items: center;
gap: 1rem;
padding: 2rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-image { width: 100px; height: 100px; }
.card-content { flex: 1; } /* text takes the remaining width */
Wrapping & Gap
By default flex items stay on a single line and shrink to fit — which can crush them uncomfortably. flex-wrap lets them flow onto new lines instead, the foundation of responsive card layouts.
.container {
flex-wrap: nowrap; /* default — everything on one line */
flex-wrap: wrap; /* items flow to new lines as needed */
flex-wrap: wrap-reverse; /* new lines stack upward */
}
Combine flex-wrap: wrap with a flex-basis and you get a gallery that reflows from many columns down to one — without a single media query:
.gallery {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.gallery-item {
/* grow, shrink, and start at ~300px wide;
items wrap automatically when they no longer fit */
flex: 1 1 300px;
}
💡 Prefer gap over margins
The gap property adds space between flex items only — no leftover margin on the outer edges, and no negative-margin hacks. It's fully supported in every modern browser. Reach for gap first; use margins only for one-off nudges.
When items wrap onto multiple lines, align-content controls the spacing between those lines (it has no effect on a single line):
.container {
flex-wrap: wrap;
align-content: space-between; /* space out the rows themselves */
}
Flexible Item Sizing
So far we've controlled items from the container. The real flexibility lives on the items themselves, through three properties usually written as one shorthand.
flex-grow values claim a bigger share of the leftover space.flex-grow — how greedily an item takes free space
.item {
flex-grow: 0; /* default — don't grow beyond content */
flex-grow: 1; /* grow to absorb free space */
flex-grow: 2; /* take twice the share of a flex-grow: 1 sibling */
}
flex-shrink — how readily an item gives up space
.item {
flex-shrink: 1; /* default — may shrink when space is tight */
flex-shrink: 0; /* never shrink (useful for fixed sidebars/icons) */
}
flex-basis — the item's starting size
.item {
flex-basis: auto; /* default — use the item's width/content */
flex-basis: 200px; /* start at 200px, then grow/shrink from there */
flex-basis: 25%; /* start at 25% of the container */
}
The flex shorthand (use this)
.item {
flex: 0 1 auto; /* the default: grow shrink basis */
flex: 1; /* = flex: 1 1 0 → equal-width, fully flexible columns */
flex: auto; /* = flex: 1 1 auto → flexible but content-aware */
flex: none; /* = flex: 0 0 auto → rigid, never grows or shrinks */
flex: 2 1 300px; /* custom: prefer 300px, grow with weight 2, may shrink */
}
⚠️ flex: 1 vs flex: auto
flex: 1 sets the basis to 0, so siblings become truly equal-width regardless of their content. flex: auto keeps the basis at auto, so items with more content start wider. When you want perfectly even columns, use flex: 1.
order — rearrange without touching the HTML
.item { order: 0; } /* default */
.sidebar { order: 2; } /* visually move after siblings... */
.main { order: 1; } /* ...even though sidebar comes first in the HTML */
Use order sparingly — it changes visual order but not the DOM order that screen readers and keyboard navigation follow. Reserve it for responsive tweaks where the reading order still makes sense.
Common Patterns
Sticky footer (the page shell)
Make the whole page a vertical flex column and let main grow — the footer sinks to the bottom even on short pages.
body {
display: flex;
flex-direction: column;
min-height: 100vh; /* fill at least the viewport */
margin: 0;
}
main { flex: 1; } /* absorb all leftover vertical space */
footer { padding: 1rem; }
Holy-grail layout (header, sidebar, content, footer)
<div class="holy-grail">
<header>Header</header>
<div class="hg-body">
<nav>Navigation</nav>
<main>Main Content</main>
<aside>Sidebar</aside>
</div>
<footer>Footer</footer>
</div>
.holy-grail { display: flex; flex-direction: column; min-height: 100vh; }
.hg-body { display: flex; flex: 1; }
nav, aside { flex: 0 0 200px; } /* fixed 200px rails, never grow/shrink */
main { flex: 1; } /* content fills the middle */
/* stack the three columns on narrow screens */
@media (max-width: 768px) {
.hg-body { flex-direction: column; }
nav, aside { flex: 0 0 auto; }
}
You'll often build this exact layout with Grid instead (next lesson), but seeing it in Flexbox first shows how the same result is reachable one dimension at a time.
Practice & Quiz
🏋️ Exercise 1: A responsive nav bar
Goal: Build a header with a logo on the left and three links on the right, all vertically centered, with even spacing between the links.
<header class="site-header">
<div class="logo">MySite</div>
<nav class="links">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
</header>
💡 Hint
Make .site-header a flex container with justify-content: space-between and align-items: center. Then make .links its own flex container with a gap.
✅ Solution
.site-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
}
.links {
display: flex;
gap: 1.5rem;
}
🏋️ Exercise 2: A wrapping card gallery
Goal: Lay out a set of cards that show 3-across on desktop, wrap to fewer per row as the window narrows, and never get smaller than ~250px — using no media queries.
💡 Hint
One flex container with flex-wrap: wrap and a gap. Give each card flex: 1 1 250px so it grows to fill, shrinks when needed, but prefers a 250px basis.
✅ Solution
.gallery {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.card {
flex: 1 1 250px; /* grow, shrink, 250px basis */
display: flex;
flex-direction: column; /* stack image / text / footer */
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card-content { flex: 1; padding: 1rem; } /* equal-height cards */
🎯 Quick Quiz
Question 1: Which property distributes flex items along the main axis?
Question 2: You set flex-direction: column. What does justify-content: center now do?
Question 3: Which shorthand makes several items into perfectly equal-width columns?
Best Practices & Pitfalls
✅ Do
- Use Flexbox for one-dimensional layouts — rows or columns of items
- Space items with the
gapproperty instead of margins - Prefer the
flexshorthand over setting grow/shrink/basis separately - Combine
flex-wrap: wrapwith aflex-basisfor responsive galleries - Add
min-width: 0to a flex item whose long text or images overflow
❌ Don't
- Reach for Flexbox when you truly need a 2D grid — use CSS Grid instead
- Overuse
order; it desyncs visual order from screen-reader/tab order - Hard-code widths on items you want to be flexible — let
flexdo the sizing - Forget that alignment properties act on axes, which rotate with
flex-direction
⚠️ The overflow gotcha
Flex items have a default min-width: auto, meaning they refuse to shrink below their content's size. A long word or a wide image can then blow out your layout. The fix is to add min-width: 0 (or overflow: hidden) to the offending item so it can shrink properly.
.text-column {
flex: 1;
min-width: 0; /* allow shrinking so long text wraps instead of overflowing */
}
Summary
🎉 Key Takeaways
- Flexbox is for one-dimensional layouts; set
display: flexon the parent and its direct children become flex items - Everything is defined by the main axis (
justify-content) and the cross axis (align-items) — andflex-directionrotates them justify-content: center+align-items: centercenters anything in three linesflex-wrap: wrapplusgapand aflex-basisbuilds responsive galleries with no media queries- Size items with the
flexshorthand:flex: 1for equal columns,flex: nonefor rigid ones
📚 Additional Resources
🚀 What's Next?
Flexbox mastered the line. Next you'll add the second dimension: CSS Grid basics lets you place items into rows and columns at once, unlocking full-page layouts, named template areas, and truly responsive grids.
🎉 Great work!
You can now center anything, build a nav bar in one line, and make cards reflow on their own. Flexbox will be in almost every project you ever ship.