Pseudo-Classes

📦 Chapter 8 — Flexbox

Before Flexbox, making elements sit neatly side by side — or perfectly centred, or evenly spaced — required hacks involving floats, inline-block, and tables. Flexbox arrived to solve exactly this problem. It is a one-dimensional layout system: you decide on a direction (row or column) and then Flexbox handles the distribution of space, alignment, and ordering of everything inside. It has become the most widely-used CSS layout tool, and understanding it will unlock the majority of real-world layouts.

1 — The Container and Its Items

Flexbox involves two roles:

  • The flex container — the parent element you set display: flex on. It controls how its children are arranged.
  • Flex items — the direct children of the flex container. They respond to the container's rules and can also control their own size and alignment.

Properties are split between the two roles. Container properties control the overall layout strategy; item properties control individual item behaviour within that strategy.

Container properties
display: flex
flex-direction
justify-content
align-items
align-content
flex-wrap
gap
Item properties
flex-grow
flex-shrink
flex-basis
flex (shorthand)
align-self
order
🎨 CSS — Activating Flexbox
.container { display: flex; /* block-level flex container */ } /* All direct children immediately become flex items. They no longer need display: inline-block — flex handles sizing. */ .inline-container { display: inline-flex; /* flex container that sits inline with text */ }

2 — The Two Axes

Every Flexbox layout has two axes:

  • Main axis — the direction items are placed in (controlled by flex-direction). By default this is horizontal (left to right).
  • Cross axis — perpendicular to the main axis. If items run left-to-right, the cross axis runs top-to-bottom.
flex-direction: row (default)
Main axis →
Cross axis ↓
flex-direction: column
Main axis ↓
Cross axis →
justify-content controls the main axis. align-items controls the cross axis.
Remember: justify-content = main axis. align-items = cross axis. When flex-direction changes, both axes swap — so justify-content in a column container distributes items vertically.

3 — flex-direction

Sets which direction items flow and which end they start from. The default is row.

row (default) — left to right
A
B
C
D
row-reverse — right to left
A
B
C
D
column — top to bottom
A
B
C
🎨 CSS — flex-direction values
.container { display: flex; flex-direction: row; /* default — left to right */ flex-direction: row-reverse; /* right to left */ flex-direction: column; /* top to bottom */ flex-direction: column-reverse; /* bottom to top */ }
row-reverse and column-reverse affect visual order only — they do not change the tab/reading order in the DOM, which can cause accessibility problems if overused.

4 — justify-content (main axis)

Controls how items are distributed along the main axis when there is extra space in the container.

Live demos — all six justify-content values
flex-start
A
B
C
flex-end
A
B
C
center
A
B
C
space-between
A
B
C
space-around
A
B
C
space-evenly
A
B
C
🎨 CSS — justify-content
.container { display: flex; justify-content: flex-start; /* pack to the start (default) */ justify-content: flex-end; /* pack to the end */ justify-content: center; /* pack to the centre */ justify-content: space-between; /* gaps between; none at edges */ justify-content: space-around; /* half-gap at edges */ justify-content: space-evenly; /* equal gaps everywhere */ }
💡 space-between vs space-evenly: space-between is the most used — logo left, nav right. space-evenly produces equal space everywhere including the outer edges, which makes it ideal for icon grids or tab bars.

5 — align-items (cross axis)

Controls how items are aligned along the cross axis — vertically when items flow in a row, horizontally when they flow in a column.

Live demos — items have different heights to show the difference
flex-start
A (tall)
B
C
center
A (tall)
B
C
flex-end
A (tall)
B
C
stretch
A
B
C
🎨 CSS — align-items
.container { display: flex; align-items: stretch; /* default — items fill the cross axis */ align-items: flex-start; /* align to the start of the cross axis */ align-items: flex-end; /* align to the end */ align-items: center; /* vertically centre (in a row container) */ align-items: baseline; /* align by text baseline */ }
The combination justify-content: center; align-items: center; on a flex container is the quickest and most reliable way to perfectly centre a single child element — horizontally and vertically simultaneously.

6 — flex-wrap and gap

By default, flex items try to fit on a single line — if there are too many to fit, they shrink. flex-wrap: wrap lets them spill onto additional lines instead.

nowrap (default) — items shrink to stay on one line
Alpha
Bravo
Charlie
Delta
Echo
wrap — items move onto new lines when they can't fit
Alpha
Bravo
Charlie
Delta
Echo
🎨 CSS — flex-wrap and gap
.container { display: flex; flex-wrap: nowrap; /* default — single line, items shrink */ flex-wrap: wrap; /* wrap onto new lines */ flex-wrap: wrap-reverse; /* wrap, but new lines appear above */ /* gap — space between flex items (not at the edges) */ gap: 16px; /* same gap in both directions */ gap: 12px 20px; /* row-gap column-gap */ row-gap: 12px; /* vertical gap between wrapped rows */ column-gap: 20px; /* horizontal gap between items */ }
gap only adds space between items — never at the edges of the container. This is much cleaner than using margin on every child, which would create double-gaps between items and extra space at the edges.

7 — flex-grow, flex-shrink, flex-basis

These three item properties control how a flex item sizes itself relative to the available space:

  • flex-basis — the ideal starting size before free space is distributed. Can be a length (200px), a percentage, or auto (use the item's content size).
  • flex-grow — how much of the remaining free space this item claims, relative to its siblings. A value of 1 means "take an equal share". A value of 2 means "take twice as much as a sibling with 1".
  • flex-shrink — how much this item shrinks when there is not enough space. Default is 1 (all items shrink equally). Set to 0 to prevent shrinking.
flex-grow demo — item C (grow: 2) takes twice the free space as A and B (grow: 1)
A grow:1
B grow:1
C grow:2
🎨 CSS — flex-grow, flex-shrink, flex-basis and the flex shorthand
/* Long form */ .item { flex-grow: 1; /* claim free space proportionally */ flex-shrink: 1; /* shrink proportionally when space is tight */ flex-basis: auto; /* start from the item's natural content size */ } /* The flex shorthand — use this in practice */ .item { flex: 1; } /* flex: 1 1 0 — grow, shrink, start from 0 */ .item { flex: auto; } /* flex: 1 1 auto — grow, shrink, use content size */ .item { flex: none; } /* flex: 0 0 auto — rigid, does not grow or shrink */ .item { flex: 0 0 200px; } /* fixed 200px — no growing, no shrinking */ /* Equal-width columns — the most common flex pattern */ .three-col .col { flex: 1; } /* each column takes one equal share */ /* Sidebar + main content layout */ .sidebar { flex: 0 0 240px; } /* fixed 240px sidebar */ .main { flex: 1; } /* main takes all remaining space */
When using the flex shorthand, flex: 1 sets flex-basis to 0 (not auto), meaning items all start from zero and divide the total space equally regardless of content. This is usually what you want for equal-width columns.

8 — align-self and order

align-self

Overrides the container's align-items value for a single item. All the same values apply: flex-start, center, flex-end, stretch, baseline.

Container has align-items: flex-start — item B overrides to align-self: flex-end
A (start)
B (self: end)
C (self: center)
D (stretch)
🎨 CSS — align-self
.container { display: flex; align-items: flex-start; } /* Override for individual items */ .special { align-self: center; } /* vertically centred */ .pinned { align-self: flex-end; } /* pinned to the bottom */ .full { align-self: stretch; } /* stretches to fill row height */

order

Changes the visual display order of flex items without changing the HTML. Items are sorted by their order value (lowest first). The default is 0.

HTML order is A, B, C, D — CSS order rearranges visually to C, A, D, B
A (order: 2)
B (order: 4)
C (order: 1)
D (order: 3)
⚠️ order changes visual order only, not DOM order. Screen readers and keyboard tab order still follow the HTML. Use order for decorative rearrangements (e.g. reversing a card layout on mobile) — never to fix a logical reading sequence that should be corrected in the HTML itself.

9 — Practical Patterns

Navigation bar — logo left, links right

Live demo
🎨 CSS
.nav { display: flex; justify-content: space-between; align-items: center; padding: 0 20px; height: 56px; } .nav-links { display: flex; gap: 24px; }

Centred hero section

Live demo

Welcome to the course

Learn CSS from the ground up with real examples.

🎨 CSS
.hero { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; min-height: 60vh; gap: 16px; }

Equal-width card row with a "Read more" pinned to the bottom

Live demo — cards are equal width; "Read more" links are pinned to the bottom of each card regardless of content length
🎨
CSS Fundamentals
Learn the core properties that every web developer uses daily.
Read more →
📐
Layout Systems
Master Flexbox and Grid to build any layout you can imagine. The foundation of modern responsive design.
Read more →
Animations
Add motion and life to your interfaces with transitions.
Read more →
🎨 CSS — The "pinned footer link" card pattern
.card-row { display: flex; gap: 16px; } .card { flex: 1; /* equal widths */ display: flex; /* nested flex container */ flex-direction: column; padding: 16px; } .card-description { flex: 1; /* grows to push the link to the bottom */ } .card-link { margin-top: auto; /* alternative: auto margin pushes it to the bottom */ }
margin-top: auto on a flex item is a powerful trick — it absorbs all the remaining free space on the main axis, pushing the element to the far end of the container. Perfect for pinning buttons to the bottom of cards of varying height.

10 — Quick Reference

Container

PropertyValuesWhat it does
displayflex / inline-flexActivates Flexbox on the container
flex-directionrow · row-reverse · column · column-reverseSets the main axis direction
justify-contentflex-start · flex-end · center · space-between · space-around · space-evenlyDistributes items along the main axis
align-itemsstretch · flex-start · flex-end · center · baselineAligns items along the cross axis
flex-wrapnowrap · wrap · wrap-reverseControls whether items wrap onto new lines
gaplength / two lengthsSpace between items (row-gap column-gap)
align-contentSame as align-items + space-between etc.Aligns wrapped rows along the cross axis (requires wrap)

Items

PropertyValuesWhat it does
flexflex: grow shrink basisShorthand for all three sizing properties
flex-grownumber (default 0)How much free space this item claims
flex-shrinknumber (default 1)How much this item shrinks when space is tight
flex-basislength / autoThe item's ideal starting size
align-selfSame as align-items values + autoOverrides align-items for this single item
orderinteger (default 0)Visual order — lower values appear first

✏️ Exercises

Work through these in order — each one builds intuition for a different aspect of Flexbox. Use DevTools to inspect the computed flex values and watch the layout update as you change properties live.

Exercise 1
Build a responsive toolbar. Create a <div class="toolbar"> containing: a logo/brand name on the left, a group of three icon-buttons in the centre, and a "Log in" button on the right. The three icon-buttons should have equal spacing between them. The whole toolbar should be 60px tall and vertically centre all its children. Use only Flexbox — no positioning, no floats.
Hint: use justify-content: space-between on the toolbar to push the three groups to the edges, then nest a second flex container inside for the centre icon group with gap. Alternatively, use flex: 1 on the left and right sections and justify-content: center on the icon group.
HTML
<div class="toolbar"> <div class="tb-brand">⬡ MySite</div> <div class="tb-icons"> <button>🔍</button> <button>🔔</button> <button>⚙️</button> </div> <div class="tb-actions"> <button class="login-btn">Log in</button> </div> </div>
CSS
.toolbar { display: flex; align-items: center; justify-content: space-between; height: 60px; padding: 0 20px; background: #12141e; border-radius: 6px; } .tb-brand { font-weight: 700; color: #4f8ef7; font-family: system-ui, sans-serif; } .tb-icons { display: flex; gap: 8px; } .tb-icons button { background: none; border: 1px solid #2a2d3a; border-radius:6px; padding: 6px 10px; cursor: pointer; font-size: 1rem; } .login-btn { background: #4f8ef7; color: #fff; border: none; border-radius:5px; padding: 8px 16px; font-weight: 600; font-family: system-ui, sans-serif; cursor: pointer; }
Exercise 2
Build a two-column layout: a fixed-width sidebar (250px) on the left and a flexible main content area that fills the remaining space on the right. Both columns should stretch to the same height. Inside the main area, add a header, a body paragraph, and a footer bar with two buttons — the footer buttons should sit at opposite ends of the footer using Flexbox. This exercise involves nesting flex containers.
Hint: the outer layout uses display: flex. The sidebar uses flex: 0 0 250px and the main content uses flex: 1. The inner content area is itself a column-direction flex container so the footer can be pushed to the bottom with margin-top: auto.
HTML
<div class="layout"> <aside class="sidebar"> <p>Sidebar navigation</p> </aside> <main class="main-content"> <h2>Page Title</h2> <p>Main content goes here.</p> <div class="footer-bar"> <button>Cancel</button> <button class="primary">Save</button> </div> </main> </div>
CSS
.layout { display: flex; gap: 20px; min-height: 400px; } .sidebar { flex: 0 0 250px; background: #1a1d27; border: 1px solid #2a2d3a; border-radius:6px; padding: 16px; } .main-content { flex: 1; display: flex; flex-direction: column; background: #1a1d27; border: 1px solid #2a2d3a; border-radius: 6px; padding: 20px; } .footer-bar { display: flex; justify-content: space-between; margin-top: auto; /* pin to the bottom */ padding-top: 16px; border-top: 1px solid #2a2d3a; }
Exercise 3
Create a wrapping tag cloud. Generate at least 12 <span class="tag"> elements with different text lengths (single words, two-word phrases, etc.). Use display: flex; flex-wrap: wrap; gap: 8px; on the container. The tags should naturally flow to new rows when they run out of horizontal space. Give each tag a pill shape with padding and a rounded border. Hover state should change the background colour.
Hint: the tags do not need flex: 1 — leave them at their natural size so they wrap organically. Try resizing your browser window to watch them reflow.
CSS
.tag-cloud { display: flex; flex-wrap: wrap; gap: 8px; } .tag { padding: 5px 14px; border-radius: 999px; border: 1px solid #2a2d3a; background: #1a1d27; color: #a0a4b8; font-family: system-ui, sans-serif; font-size: 0.85rem; cursor: pointer; transition: background 0.15s, color 0.15s; } .tag:hover { background: #1e2a40; color: #7ab8ff; border-color:#4f8ef7; }
Exercise 4
Build a pricing table with three plans (Basic, Pro, Featured). The Featured plan should be visually highlighted with a different border and background, and should appear in the middle. Use Flexbox to arrange the three cards side by side with equal widths. The middle (Featured) card should be taller than the others — achieve this using align-self: stretch and a larger padding, or use align-items: flex-start on the container and an explicit larger min-height on the featured card. Each card should have a plan name, price, a short feature list, and a CTA button pinned to the bottom.
Hint: use align-items: stretch (the default) on the container so all cards fill the row height, then give the featured card more padding to make it look larger. Apply margin: -12px 0 to the featured card to make it protrude above and below the others.
HTML
<div class="pricing"> <div class="plan"> <h3>Basic</h3> <div class="price">£9/mo</div> <ul><li>5 projects</li><li>1 GB storage</li></ul> <button>Get started</button> </div> <div class="plan featured"> <h3>Pro</h3> <div class="price">£29/mo</div> <ul><li>Unlimited projects</li><li>50 GB storage</li><li>Priority support</li></ul> <button class="primary">Get started</button> </div> <div class="plan"> <h3>Enterprise</h3> <div class="price">£99/mo</div> <ul><li>Unlimited everything</li><li>SLA guarantee</li></ul> <button>Contact sales</button> </div> </div>
CSS
.pricing { display: flex; gap: 16px; } .plan { flex: 1; display: flex; flex-direction: column; background: #1a1d27; border: 1px solid #2a2d3a; border-radius: 8px; padding: 20px; font-family: system-ui, sans-serif; } .plan.featured { background: #1a2240; border-color:#4f8ef7; padding: 28px 20px; margin: -12px 0; /* protrudes above and below */ } .plan button { margin-top: auto; /* pin to bottom */ padding: 10px; border: 1px solid #2a2d3a; border-radius:5px; cursor: pointer; font-weight: 600; background: #1e2235; color: #e2e4ec; } .plan button.primary { background: #4f8ef7; color: #fff; border-color: #4f8ef7; }