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.

⚠️ align-content only works with multiple lines. If 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.
Live demo — align-content (multi-line, fixed height container)
A
B
C
D
E
F
G
align-content: flex-start;
ValueBehaviour
flex-startLines packed at the start of the cross axis. Free space at the end.
flex-endLines packed at the end. Free space at the start.
centerLines grouped in the centre of the cross axis.
space-betweenFirst line at start, last at end. Remaining space shared equally between lines.
space-aroundEqual space on each side of every line. Edge gaps half the size of inner gaps.
space-evenlyEqual gaps between all lines and at both cross-axis edges.
stretch defaultLines stretch to fill the container's cross-axis size evenly.
align-items vs align-content — the distinction:
  • align-items — positions individual items within their flex line
  • align-content — positions the flex lines themselves within the container
Both can be active simultaneously: 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.

Live demo — order (DOM order: A, B, C, D, E)
A
B
C
D
E
/* all items: default order (0) — DOM order */
🎨 CSS — Using 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 only changes visual order, not DOM order. Screen readers, tab navigation, and search engines follow the HTML source order — not the visual order. If you use 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.

Live demo — auto margins (3 items, row container)
A
B
C
/* no auto margins — default layout */

The most common real-world uses:

🎨 CSS — Auto margin patterns
/* ── 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 */ }
Auto margins beat justify-content when you only want to push one specific item while leaving the others in their natural position.
💡 Auto margins override justify-content. When a flex item has an 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.

Example: 600px container, three items with flex-basis: 0
A (grow: 1)
B (grow: 2)
C (grow: 1)
Total grow units: 1 + 2 + 1 = 4
Space per unit: 600px ÷ 4 = 150px
A gets: 1 × 150px = 150px   B gets: 2 × 150px = 300px   C gets: 1 × 150px = 150px
🎨 CSS — flex-grow example
.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).

Example: 400px container, three items each with flex-basis: 200px (600px total — 200px overflow)
A: shrinks ~67px → ~133px
B: shrinks ~67px → ~133px
C: shrinks ~67px → ~133px
Overflow: 600px − 400px = 200px to give back
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)
Preventing shrinkage is common for fixed-width elements. A sidebar, a logo, an icon button — anything that should stay a specific size regardless of how crowded the container gets should have 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-basis: auto
Starts from natural size
The item first takes up space for its content, then distributes any remaining free space. Items with more content start larger before growing. Used in flex: 1 1 auto.
flex-basis: 0
Starts from zero
All space is treated as "free space" to be distributed by flex-grow. Items grow proportionally from scratch, ignoring content size. Used in flex: 1 (shorthand for 1 1 0).
flex-basis: 200px
Starts from a fixed size
Item begins at exactly 200px along the main axis, then grows or shrinks from there. Overrides width (in a row container). Use for items that need a minimum guaranteed size before growing.
flex-basis: content
Exactly the content size
Similar to auto but always uses the content's intrinsic size, ignoring any explicit width or height. Useful but less widely supported in older browsers.
🎨 CSS — flex-basis: 0 vs auto side-by-side
/* 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; }
Use 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.

🎨 CSS — Fixing overflow caused by min-width: auto
/* 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; }
If you have a flex item that contains a code block, a long URL, a table, or any other wide content, and the layout breaks unexpectedly, 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.

🎨 CSS — A three-panel layout with nested flex
/* 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; }
💡 When to stop nesting and use Grid instead. If you find yourself nesting more than two or three flex containers to create a layout, consider switching to CSS Grid for the outer structure. Grid handles two-dimensional layouts more naturally. Flexbox excels for one-dimensional component interiors — button groups, nav links, form rows, card contents.

8 — Responsive Flexbox Patterns

Pattern 1: Row → column on small screens

🎨 CSS
.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)

🎨 CSS
.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% */ }
With 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

🎨 CSS
.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

🎨 CSS
/* 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

PropertySet onWhat it does
align-contentContainerDistributes wrapped flex lines along the cross axis. Requires flex-wrap: wrap and multiple lines.
orderItemInteger controlling visual position. Default: 0. Lower = earlier. Does not change DOM order.
margin-[side]: autoItemAbsorbs all free space in that direction. Can push other items away or centre a single item.
min-width: 0ItemOverrides the default min-width: auto so the item can shrink below its content size.

align-content values (same pattern as justify-content)

ValueLines distributed as
stretch defaultLines expand to fill available cross-axis space
flex-startLines packed at cross-axis start
flex-endLines packed at cross-axis end
centerLines grouped in the middle
space-betweenFirst/last lines at edges, equal gaps between remaining lines
space-aroundEqual space on each side of every line
space-evenlyIdentical gaps between all lines and at edges

✏️ Exercises

Build each layout in a fresh HTML file. Try the task before checking the solution.

Exercise 1
Create a fixed-height container (300px) with 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.
Hint: Set a fixed height on the container, 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.
CSS
.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; }
Exercise 2
Build a navigation bar with four links on the left (Home, About, Services, Blog) and a Sign In button on the far right — all without using justify-content: space-between. Use an auto margin instead.
Hint: Put all five items in a single flex row. Apply margin-left: auto to the Sign In button — it absorbs all free space to its left, pushing it to the right edge.
HTML
<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>
CSS
.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; }
Exercise 3
Create a flex row with four items where the widths are proportional: the second item should be twice as wide as the first and third, and the fourth should be three times as wide. All items should grow from zero (not from their content size). No fixed widths — use flex-grow only.
Hint: Set 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.
CSS
.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 */
Exercise 4
Build a mobile-first feature section. On small screens (default), three feature blocks should stack in a column with text content first. On wide screens (min-width: 768px), they should sit in a row. Add a media query that also reverses the order of the first block — so its image appears on the right on desktop — without changing the HTML.
Hint: Start with 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.
HTML
<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>
CSS
.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; } }