Lesson 21 • Advanced Track
CSS Flexbox: Advanced Patterns & Real Layouts
By the end of this lesson you'll control exactly how flex items grow, shrink, and reorder themselves — and use that control to build the layouts real apps ship with: a sticky footer, the holy-grail page, and equal-height responsive card grids.
What You'll Learn
display: flex, the difference between the main and cross axis, and justify-content / align-items. If any of that feels shaky, review Flexbox Basics first, then come back for the advanced control.💡 Think of It Like This
A flex container is a row of passengers sharing a bench seat. flex-basis is how much room each person needs before anyone budges. flex-grow is appetite for the empty space — a passenger with flex-grow: 2 spreads out into twice as much of the leftover bench as a neighbour on flex-grow: 1.
flex-shrink is who gives way when the bench gets crowded: a higher number means "I'll squeeze in first." So the flex shorthand — grow shrink basis — is really just three answers to: how big do I start, how eagerly do I expand, and how willingly do I compress? Nail those three and every layout in this lesson becomes obvious.
1. The flex Shorthand: grow, shrink, basis
Three properties decide how a flex item is sized. flex-basis is the item's starting size along the main axis (its width in a row). flex-grow says how much of any leftover space it absorbs, as a ratio against its siblings. flex-shrink says how much it gives up when there isn't enough room. You almost always set all three at once with the flex shorthand.
| Shorthand | Means | Use it for |
|---|---|---|
flex: 1 | 1 1 0% | Equal columns, ignoring content size |
flex: auto | 1 1 auto | Grow/shrink but respect content size |
flex: none | 0 0 auto | Fixed, never grows or shrinks |
flex: 0 0 200px | 0 0 200px | A rigid 200px sidebar |
Run the worked example. The same three boxes are shown with different flex values so you can see grow ratios, a fixed item, and a basis-driven start size side by side.
Worked example: flex-grow, flex-shrink, flex-basis
Three rows showing grow ratios, a fixed item, and flex-basis
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flex shorthand</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif; padding:20px; }
h3 { color:#3b82f6; margin:18px 0 8px; font-size:14px; }
.row { display:flex; gap:8px; margin-bottom:6px; }
.box { padding:14px; border-radius:8px; color:#fff; font-size:13px; text-align:center; }
.a { background:#3b82f6;
...2. Overriding One Item: align-self & order
align-items aligns every item on the cross axis. When you want one item to break ranks, align-self on that single child overrides the container's alignment just for it — handy for pushing one badge to the top while the rest sit centred.
order changes the visual position of an item without touching the HTML. Items sort by their order value (default 0), low to high. It is perfect for reordering on different screen sizes — but remember it only moves things visually, so keyboard and screen-reader order still follow your HTML.
Worked example: align-self and order
One item breaks the alignment; order reshuffles without editing HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>align-self & order</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif; padding:20px; }
h3 { color:#3b82f6; margin:18px 0 8px; font-size:14px; }
.row {
display:flex;
align-items:center; /* default: all items vertically centred */
gap:10px; height:120px;
background:#1e293b; border-radius
...3. Wrapping: flex-wrap, align-content & gap
By default flex items stay on one line and shrink to fit. Set flex-wrap: wrap and items that don't fit flow onto a new line instead — this is what turns a flex row into a responsive grid. Pair it with gap, which adds consistent spacing between items (and between wrapped rows) without the margin headaches.
Once items wrap onto multiple lines, align-content spaces those lines along the cross axis — think of it as justify-content but for whole rows. It does nothing on a single-line container, which trips up a lot of people. Run the example and shrink the preview to watch items wrap and the rows space out.
Worked example: flex-wrap + align-content + gap
Items wrap to new lines; align-content distributes the rows
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>flex-wrap</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif; padding:20px; }
p { color:#94a3b8; font-size:13px; margin-bottom:10px; }
.wrap {
display:flex;
flex-wrap: wrap; /* let items spill onto new lines */
...4. Nested Flex: the Holy-Grail Layout
Real layouts are flex containers inside flex containers. The classic "holy grail" — header, two sidebars, a main column, and a footer — is just a vertical flex column whose middle row is itself a horizontal flex row. The outer column uses flex: 1 on the middle band so it fills the height; the inner row uses fixed-basis sidebars and a flex: 1 main.
Note min-width: 0 on the main column — without it, long content would refuse to shrink and blow out the layout (more on that in Common Errors). On narrow screens, flex-wrap and a media query collapse everything into a single column.
Worked example: holy-grail layout
Header, sidebar, main, sidebar, footer — nested flex
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Holy Grail</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif;
display:flex; flex-direction:column; /* OUTER: vertical column */
min-height:100vh;
}
header, footer { background:#1e293b; padding:15px 20px; text-align:cent
...5. Equal-Height Card Grid
Cards with different amounts of text usually end up different heights — ugly. Flexbox fixes it twice over. First, align-items: stretch (the default on the cross axis) makes every card in a row the same height automatically. Second, make each card itself a vertical flex column and put margin-top: auto on the footer button — the auto margin soaks up spare space and pins the button to the bottom, so buttons line up across cards of any length.
Worked example: equal-height cards with aligned buttons
flex-wrap grid + nested column + margin-top:auto footer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Equal-height cards</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif; padding:24px; }
h1 { color:#3b82f6; margin-bottom:18px; font-size:1.3rem; }
.grid {
display:flex; flex-wrap:wrap; gap:16px;
align-items: stretch; /* every
...🎯 Your Turn #1 — A fixed sidebar with the flex shorthand
Build a two-column row: a rigid 220px sidebar that never resizes, and a main column that fills the rest. Fill in the blanks marked ___, run it, and check the expected result in the comments.
Your Turn #1: fixed sidebar + flexible main
Use flex: 0 0 220px and flex: 1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Your Turn 1</title>
<style>
/* 🎯 YOUR TURN — fill in the blanks marked ___ */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif; padding:20px; }
.layout { display:flex; gap:12px; }
.sidebar {
background:#1e293b; padding:16px; border-radius:10px;
/* 1) Make this a RIGID 220px column: no grow, no shrink, basi
...🎯 Your Turn #2 — Wrap a grid and align one item
Turn a flex row into a wrapping grid with even spacing, then make the "featured" tile jump to the front using order. Fill in the blanks and verify the expected behaviour in the comments.
Your Turn #2: flex-wrap + gap + order
Make items wrap, space them, and reorder one
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Your Turn 2</title>
<style>
/* 🎯 YOUR TURN — fill in the blanks marked ___ */
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background:#0f172a; color:#e5e7eb; font-family: system-ui, sans-serif; padding:20px; }
.grid {
display:flex;
/* 1) Let items spill onto new lines when they don't fit. */
___
...🧩 Mini-Challenge — Equal-height pricing cards from scratch
Support is faded now — only an outline is given. Build a row of pricing cards that stay equal height with their buttons aligned along the bottom. Use the worked examples in sections 3 and 5 as your reference if you get stuck.
Mini-Challenge: pricing cards
flex-wrap grid of equal-height cards with bottom-aligned buttons
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mini-Challenge</title>
<style>
/* 🧩 MINI-CHALLENGE: a row of equal-height pricing cards
1. Make a .pricing container: display:flex, flex-wrap:wrap, gap:16px
2. Give each .plan card flex: 1 1 200px so they wrap on small screens
3. Make each .plan a NESTED column: display:flex; flex-direction:column
4. Put margin-top
...⚠️ Common Errors (and the fix)
- Flex item overflows the page / won't shrink. Every flex item has
min-width: autoby default, so a long word, a wide image, or a<pre>block stops it shrinking and blows out the layout. Fix: addmin-width: 0(ormin-height: 0in a column) to the item. - Confusing flex-basis with width. Setting both
widthandflex-basisis contradictory —flex-basiswins for the initial main-axis size. Fix: inside a flex container, size items with theflexshorthand (e.g.flex: 1 1 280px), notwidth. - "flex: 1 made my cards tiny."
flex: 1is1 1 0%— a zero basis — so items collapse to equal fractions and ignore a minimum width. Fix: give them a real basis:flex: 1 1 280px. - align-content does nothing. It only spaces multiple lines. On a single-line container it has no effect. Fix: make sure
flex-wrap: wrapis set and there is more than one row of items. - order broke keyboard navigation.
ordermoves items visually but not in the DOM, so tab order and screen readers still follow the HTML. Fix: write the HTML in a sensible reading order and useorderonly for small visual tweaks.
📋 Quick Reference
| Goal | Use this |
|---|---|
| Equal columns (ignore content) | flex: 1; |
| Card that wraps, min 280px | flex: 1 1 280px; |
| Rigid 200px sidebar | flex: 0 0 200px; |
| Align one item differently | align-self: flex-end; |
| Reorder visually | order: -1; |
| Wrap onto new lines | flex-wrap: wrap; |
| Space wrapped rows | align-content: space-between; |
| Spacing between items | gap: 16px; |
| Pin a footer/button to bottom | margin-top: auto; |
| Stop overflow / allow shrink | min-width: 0; |
❓ Frequently Asked Questions
What is the difference between flex-basis and width?
flex-basis sets a flex item's size along the main axis (its width in a row, its height in a column) before flex-grow and flex-shrink redistribute leftover space. width is fixed and ignores that redistribution unless flex-shrink kicks in. The practical differences: flex-basis switches axis automatically with flex-direction, it wins over width when both are set on the same item, and flex-basis: 0 makes an item start from nothing so flex-grow shares space in pure ratios. Reach for flex-basis (usually via the flex shorthand) when an item lives in a flex container; reach for width when you want a hard size that grow and shrink leave alone.
Why is my flex item overflowing its container instead of shrinking?
By default every flex item has min-width: auto (or min-height: auto in a column), which means it refuses to shrink smaller than its content — a long word, a wide image, or a <pre> block sets a floor that flex-shrink cannot cross. The fix is to add min-width: 0 (or min-height: 0) to the item so it is allowed to shrink past its content and let overflow, text-overflow, or word-break handle the rest. This single line is the cure for the classic 'my flexbox blows out the page width' bug.
What does flex: 1 actually mean?
flex: 1 is shorthand for flex: 1 1 0% — flex-grow: 1, flex-shrink: 1, flex-basis: 0%. Because the basis is 0, every item starts from nothing and the whole container is shared out in proportion to the grow values, so three items with flex: 1 each become exactly equal thirds. That is different from flex: 1 1 auto (which factors in each item's content size first) and from flex: auto (which is flex: 1 1 auto). When you want equal columns regardless of content, flex: 1 is the right tool; when you want a minimum size that still grows, use flex: 1 1 280px.
Should I use order to rearrange my layout?
Use order for purely visual reordering — for example, moving a sidebar before the main content on wide screens while keeping the main content first in the HTML for screen readers and tab order. The key caveat is that order changes only the visual position, not the DOM order: keyboard focus and assistive tech still follow the source order, so a large mismatch confuses users. Use it sparingly and never as a substitute for writing the HTML in a sensible reading order.
When should I use Flexbox instead of CSS Grid?
Flexbox is one-dimensional: it lays items out along a single axis (a row or a column) and is ideal for distributing space inside that line — navbars, toolbars, button groups, centering, and content that should flow and wrap. CSS Grid is two-dimensional: it controls rows and columns at the same time and is the better choice for full page layouts and gallery grids where items must line up in both directions. A good rule of thumb: if you are thinking in a single line, use Flexbox; if you are thinking in a real grid of rows and columns, use Grid. They also compose well — a Grid page with flex containers inside its cells is common.
What is the difference between justify-content, align-items and align-content?
justify-content distributes space along the main axis (the direction your items flow). align-items aligns items along the cross axis within a single line. align-content only does something when items wrap onto multiple lines — it distributes the lines themselves along the cross axis, so on a single-line container it has no effect. If your align-content rule seems to be ignored, the usual reason is that flex-wrap is not set to wrap or there is only one line of items.
🎉 Lesson Complete
You can now bend Flexbox to any layout. The essentials:
- ✅
flex: grow shrink basis— useflex: 1for equal columns,flex: 1 1 280pxfor wrapping cards,flex: 0 0 200pxfor rigid sidebars - ✅
align-selfoverrides one item's cross-axis alignment;ordermoves it visually (not for screen readers) - ✅
flex-wrap: wrap+gapbuilds responsive grids;align-contentspaces the wrapped rows - ✅ Nest flex containers for holy-grail layouts and equal-height cards with
margin-top: auto - ✅ Remember
min-width: 0to stop content overflowing
Next up: CSS Grid Level 2, where you'll handle true two-dimensional layouts that Flexbox can't do alone.
Sign up for free to track which lessons you've completed and get learning reminders.