Flexbox Advanced
📐 Chapter 2 — Flexbox Advanced
Chapter 1 covered the core Flexbox properties — justify-content, align-items, flex-wrap, and flex. This chapter goes deeper: align-content for multi-line containers, the order property for visual reordering, auto margins as a layout superpower, and the precise mechanics behind how flex-grow and flex-shrink share space. These are the tools that separate basic Flexbox usage from fluid, production-ready layouts.
1 — align-content: Space Between Wrapped Lines
In Chapter 1 you learned that align-items positions items on the cross axis within a single flex line. When flex-wrap is on and items break into multiple rows (or columns), a second property takes over: align-content.
align-content distributes space between and around flex lines — the rows themselves — along the cross axis. It works exactly like justify-content does for items on the main axis, but for wrapped rows on the cross axis.
flex-wrap: nowrap (the default) is set, all items are on a single line and align-content has no effect whatsoever. You must have flex-wrap: wrap (or wrap-reverse) for it to do anything. This is the most common source of confusion with this property.
| Value | Behaviour |
|---|---|
flex-start | Lines packed at the start of the cross axis. Free space at the end. |
flex-end | Lines packed at the end. Free space at the start. |
center | Lines grouped in the centre of the cross axis. |
space-between | First line at start, last at end. Remaining space shared equally between lines. |
space-around | Equal space on each side of every line. Edge gaps half the size of inner gaps. |
space-evenly | Equal gaps between all lines and at both cross-axis edges. |
stretch default | Lines stretch to fill the container's cross-axis size evenly. |
align-items— positions individual items within their flex linealign-content— positions the flex lines themselves within the container
align-content sets where the rows sit in the container, and align-items sets how items sit within each row.
2 — order: Visual Reordering Without Touching HTML
By default, flex items display in the same order they appear in the HTML (DOM order). The order property overrides this — it accepts any integer (negative, zero, or positive) and items are rendered from lowest to highest order value. Items with the same order value keep their relative DOM order.
/* Default: all items have order: 0 */
/* Items with equal order render in DOM order */
.item-c { order: -1; } /* moves before all order-0 items */
.item-a { order: 1; } /* moves after all order-0 items */
/* Result: C → B → (other 0s) → A */
/* Inside a media query — reorder for mobile: */
@media (max-width: 600px) {
.sidebar { order: 2; } /* sidebar after main on mobile */
.main { order: 1; } /* main content first on mobile */
}
order to reorder content meaningfully, ensure the DOM order still makes sense for keyboard and assistive technology users. Use it for cosmetic adjustments, not to correct structural problems in your HTML.
3 — Auto Margins: The Alignment Superpower
Setting margin: auto on a flex item works differently than it does in normal block flow. In a flex container, auto margins absorb all available free space in their direction. This makes them a powerful tool for pushing items around — often more elegantly than wrapping things in extra containers.
The most common real-world uses:
/* ── Pattern 1: Push one item to the far end ── */
/* Navbar: logo + links left, CTA button right */
.navbar { display: flex; align-items: center; gap: 20px; }
.nav-cta { margin-left: auto; } /* absorbs all space, sits at far right */
/* ── Pattern 2: Split a row at a specific item ── */
/* Items before .sep left-align, items after right-align */
.sep { margin-left: auto; }
/* ── Pattern 3: Centre one item in a column ── */
.column-container {
display: flex;
flex-direction: column;
height: 300px;
}
.centred-item {
margin-top: auto;
margin-bottom: auto; /* equal space above and below = centred */
}
justify-content when you only want to push one specific item while leaving the others in their natural position.auto margin, it consumes all free space in that direction. justify-content only distributes space that items have not already claimed with auto margins — so the two interact in a predictable but important way.
4 — How flex-grow and flex-shrink Actually Work
Understanding the math behind flex sizing helps you predict and control layouts — especially when items have different sizes or grow/shrink values.
flex-grow: distributing free space
After items are placed at their flex-basis sizes, the browser calculates how much space is left over. This free space is divided among items with a positive flex-grow value in proportion to their grow numbers.
Space per unit: 600px ÷ 4 = 150px
A gets: 1 × 150px = 150px B gets: 2 × 150px = 300px C gets: 1 × 150px = 150px
.container { display: flex; width: 600px; }
.a { flex: 1 1 0; } /* gets 150px */
.b { flex: 2 1 0; } /* gets 300px */
.c { flex: 1 1 0; } /* gets 150px */
/* Shorthand: flex: 1 sets grow:1, shrink:1, basis:0 */
/* flex: 2 sets grow:2, shrink:1, basis:0 */
flex-shrink: giving back space
When items overflow the container, shrinkage kicks in. The browser calculates how much overflow exists and distributes the shrinkage among items with a positive flex-shrink value — but weighted by their flex-basis size (larger items give up more space by default).
All items: flex-shrink: 1, equal flex-basis — each gives back 200px ÷ 3 ≈ 67px
Set
flex-shrink: 0 on any item to lock it at its flex-basis (it never shrinks)
flex-shrink: 0 (or equivalently, flex: 0 0 Xpx).
5 — flex-basis: 0 vs auto vs a Fixed Value
The difference between flex-basis: 0 and flex-basis: auto is one of the most misunderstood subtleties in Flexbox.
flex: 1 1 auto.flex-grow. Items grow proportionally from scratch, ignoring content size. Used in flex: 1 (shorthand for 1 1 0).width (in a row container). Use for items that need a minimum guaranteed size before growing.auto but always uses the content's intrinsic size, ignoring any explicit width or height. Useful but less widely supported in older browsers./* flex: 1 → flex-grow:1, flex-shrink:1, flex-basis:0 */
/* All space is free space. Items grow in exact proportion. */
.equal-columns { flex: 1; }
/* flex: 1 1 auto */
/* Items start at their content size, then share remaining space. */
/* A long-text item starts wider and gains less free space than a */
/* short-text item, which starts narrow and gains more. */
.content-proportional { flex: 1 1 auto; }
flex: 1 (basis 0) for truly equal columns. Use flex: 1 1 auto when you want columns that roughly match their content size but still fill the container.6 — min-width: 0 — The Hidden Flexbox Gotcha
Flex items have an implicit min-width: auto by default. This means a flex item will never shrink below the size of its content — even if you set flex-shrink: 1. This protects content from being squashed, but it can cause layouts to overflow unexpectedly.
/* Problem: a flex item containing a long word or a wide element */
/* refuses to shrink below its content width, causing overflow. */
/* Fix 1: override min-width on the item */
.flex-item {
flex: 1;
min-width: 0; /* allows the item to shrink below content width */
}
/* Fix 2: add overflow: hidden to the item (also clips content) */
.flex-item {
flex: 1;
overflow: hidden; /* establishes a new block-formatting context */
}
/* Both fixes let long text truncate with text-overflow: ellipsis */
.flex-item p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
min-width: 0 is almost always the fix.7 — Nested Flex Containers
Any flex item can itself be a flex container. Nested flex is the standard approach for complex layouts — the outer container handles the macro structure (columns, rows), and inner containers handle the detail within each cell.
/* Outer: horizontal split — sidebar + content area */
.page {
display: flex;
min-height: 100vh;
}
.sidebar { flex: 0 0 240px; }
/* Inner: content area — stacks header, main, footer vertically */
.content {
flex: 1;
display: flex;
flex-direction: column;
}
.content header { flex: 0 0 60px; }
.content main { flex: 1; } /* takes remaining height */
.content footer { flex: 0 0 48px; }
/* Innermost: card grid inside main */
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.card-grid .card { flex: 1 1 280px; }
8 — Responsive Flexbox Patterns
Pattern 1: Row → column on small screens
.feature-row {
display: flex;
gap: 24px;
}
.feature-row .item { flex: 1; }
@media (max-width: 640px) {
.feature-row {
flex-direction: column; /* items stack vertically */
}
}
Pattern 2: Auto-wrapping card grid (no media query needed)
.card-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.card-grid .card {
flex: 1 1 280px; /* start at 280px, grow to fill, wrap when needed */
max-width: 100%; /* prevent single card filling row beyond 100% */
}
flex: 1 1 280px, items sit side by side when the container is wide enough, and automatically drop to their own rows as the viewport narrows. No media queries required for the wrapping behaviour itself.Pattern 3: Stacked mobile nav → horizontal desktop nav
.nav {
display: flex;
flex-direction: column; /* mobile-first: stack links */
gap: 4px;
}
@media (min-width: 768px) {
.nav {
flex-direction: row; /* desktop: links in a row */
gap: 24px;
align-items: center;
}
}
Pattern 4: Reordering with order in a media query
/* Desktop: image | text (DOM order) */
.feature {
display: flex;
gap: 32px;
align-items: center;
}
/* Mobile: text first, image second (better UX) */
@media (max-width: 640px) {
.feature { flex-direction: column; }
.feature .image { order: 2; }
.feature .text { order: 1; }
}
9 — Quick Reference
Properties covered in this chapter
| Property | Set on | What it does |
|---|---|---|
align-content | Container | Distributes wrapped flex lines along the cross axis. Requires flex-wrap: wrap and multiple lines. |
order | Item | Integer controlling visual position. Default: 0. Lower = earlier. Does not change DOM order. |
margin-[side]: auto | Item | Absorbs all free space in that direction. Can push other items away or centre a single item. |
min-width: 0 | Item | Overrides the default min-width: auto so the item can shrink below its content size. |
align-content values (same pattern as justify-content)
| Value | Lines distributed as |
|---|---|
stretch default | Lines expand to fill available cross-axis space |
flex-start | Lines packed at cross-axis start |
flex-end | Lines packed at cross-axis end |
center | Lines grouped in the middle |
space-between | First/last lines at edges, equal gaps between remaining lines |
space-around | Equal space on each side of every line |
space-evenly | Identical gaps between all lines and at edges |
✏️ Exercises
Build each layout in a fresh HTML file. Try the task before checking the solution.
flex-wrap: wrap containing eight items. Use align-content: space-between to push the first row to the top and the last row to the bottom, with equal space distributed between any middle rows.flex-wrap: wrap, and align-content: space-between. Give each item a flex: 0 0 100px so they have a defined width and wrap naturally..wrap-grid {
display: flex;
flex-wrap: wrap;
align-content: space-between;
height: 300px;
gap: 10px;
padding: 16px;
background: #1a1d27;
border-radius: 8px;
}
.wrap-grid .item {
flex: 0 0 100px;
height: 60px;
background: #4f8ef7;
border-radius: 4px;
}
justify-content: space-between. Use an auto margin instead.margin-left: auto to the Sign In button — it absorbs all free space to its left, pushing it to the right edge.<nav class="navbar">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Services</a>
<a href="#">Blog</a>
<a href="#" class="btn-signin">Sign In</a>
</nav>
.navbar {
display: flex;
align-items: center;
gap: 24px;
padding: 0 24px;
height: 60px;
background: #1a1d27;
}
.navbar a {
color: #e2e4ec;
text-decoration: none;
}
.btn-signin {
margin-left: auto; /* key: absorbs all free space to its left */
padding: 7px 18px;
background: #4f8ef7;
border-radius: 5px;
color: white;
}
flex-grow only.flex: 1 1 0 on the first and third items (or just flex: 1), flex: 2 1 0 (or flex: 2) on the second, and flex: 3 1 0 (or flex: 3) on the fourth. The basis must be 0 for pure proportional sizing..prop-row {
display: flex;
gap: 8px;
height: 60px;
}
/* Total grow units: 1 + 2 + 1 + 3 = 7 */
.item-1 { flex: 1; } /* 1/7 of container */
.item-2 { flex: 2; } /* 2/7 of container */
.item-3 { flex: 1; } /* 1/7 of container */
.item-4 { flex: 3; } /* 3/7 of container */
flex-direction: column. The media query switches to row. Use order on the image and text inside the first feature block to swap them on desktop.<section class="features">
<div class="feature feature-highlight">
<div class="feature-text"><h2>Main Feature</h2><p>Description...</p></div>
<div class="feature-image">[image]</div>
</div>
<div class="feature"><h2>Feature Two</h2><p>Description...</p></div>
<div class="feature"><h2>Feature Three</h2><p>Description...</p></div>
</section>
.features {
display: flex;
flex-direction: column;
gap: 24px;
}
.feature-highlight {
display: flex;
flex-direction: column;
gap: 16px;
}
@media (min-width: 768px) {
.features {
flex-direction: row;
align-items: stretch;
}
.feature-highlight {
flex-direction: row;
align-items: center;
}
/* Image visually on the right, text on the left */
.feature-highlight .feature-text { order: 1; }
.feature-highlight .feature-image { order: 2; }
}