Flexbox Fundamentals

📦 Chapter 1 — Flexbox Fundamentals

Before Flexbox, laying out elements side by side meant fighting with floats, inline-block tricks, and table hacks — all fragile and difficult to reason about. Flexbox changed everything. It is a one-dimensional layout model that gives precise, readable control over how items are placed, sized, and aligned along a single axis. Master Flexbox and you can build navigation bars, card rows, centred heroes, and sidebar layouts with clean, declarative CSS.

1 — What is Flexbox?

The Flexible Box Layout (Flexbox) is a CSS layout mode designed for arranging items in a single line — either a row or a column. Unlike the normal document flow, flex items do not collapse, float away, or behave differently based on whether they are block or inline elements. They simply line up and respond to a consistent set of layout rules.

Flexbox works through two roles:

Flex Container
The parent element
Set display: flex on this element. It controls the direction, alignment, and spacing of its children. Layout rules are declared here.
Flex Items
The direct children
Any direct child of a flex container automatically becomes a flex item. Grandchildren are unaffected. Items can control their own sizing with the flex property.
One dimension at a time. Flexbox handles either a row or a column — not both simultaneously. For layouts that need rows and columns at once (like a full page grid), use CSS Grid, which is covered in Chapters 3 and 4 of this course. Flexbox and Grid are complementary, not competing.

2 — Creating a Flex Container

A single declaration transforms an element into a flex container:

🎨 CSS — Turning on Flexbox
/* Before: children stack vertically (normal block flow) */ .nav { /* no special display — each child starts on its own line */ } /* After: children line up in a row */ .nav { display: flex; }
display: flex makes the container itself a block-level box (takes up the full width of its parent). Use display: inline-flex if you need the container to shrink-wrap its content and sit inline with surrounding text.
🌐 HTML — A navigation with three links
<nav class="navbar"> <a href="#">Home</a> <a href="#">About</a> <a href="#">Contact</a> </nav>
🎨 CSS — Flex turns the three links into a row
.navbar { display: flex; gap: 24px; padding: 12px 20px; background: #1a1d27; } .navbar a { color: #e2e4ec; text-decoration: none; }
The three <a> elements are direct children of .navbar and automatically become flex items. They line up in a row with 24px gaps between them — no float, no inline-block, no clearfix.

3 — Main Axis and Cross Axis

Every flex container has two axes. All of Flexbox's alignment and distribution properties are defined in terms of these axes, so understanding them is the foundation of everything else.

flex-direction: row (default) — axes visualised
A
B
C
← main axis
Main axis — runs in the direction of flex-direction. Items are placed along it. justify-content distributes space here.
Cross axis — perpendicular to the main axis (vertical when direction is row). align-items and align-self act here.

When you change flex-direction to column, the axes rotate: the main axis becomes vertical (top-to-bottom) and the cross axis becomes horizontal. justify-content and align-items always follow their respective axes — they do not change meaning, only direction.

The axes rotate together. A common mistake is thinking justify-content: center always centres horizontally. It centres along the main axis. In a column container, that means vertical centering. Keep the axes in mind, not compass directions.

4 — flex-direction: Which Way Do Items Flow?

flex-direction sets the direction of the main axis — and therefore the direction in which flex items are placed. The default is row (left to right).

Live demo — flex-direction
A
B
C
flex-direction: row;
row
★ default
Items flow left → right. Main axis is horizontal.
row-reverse
Items flow right → left. Same horizontal axis but start and end are swapped.
column
Items flow top → bottom. Main axis becomes vertical.
column-reverse
Items flow bottom → top. Vertical main axis, reversed.
💡 column is key for responsive design. A row of cards on desktop becomes a stacked column on mobile with a single media query that changes just flex-direction. No restructuring the HTML.

5 — justify-content: Space Along the Main Axis

justify-content controls how the browser distributes flex items and any leftover space along the main axis. It only has a visible effect when there is free space — either because items do not fill the container, or because some items have a smaller flex-grow value than others.

Live demo — justify-content
A
B
C
justify-content: flex-start;
ValueBehaviour
flex-start defaultItems packed at the start. All free space appears at the end.
flex-endItems packed at the end. Free space appears at the start.
centerItems grouped in the centre. Equal free space at both ends.
space-betweenFirst item at start, last at end. Remaining space split equally between items. No space at edges.
space-aroundEqual space on each side of every item. Edge gaps are half the size of inner gaps.
space-evenlyIdentical gaps between all items and at both edges.
Most-used in real projects: space-between for navbar (logo left, links right) and card rows. center for hero content or centred button groups. flex-end for aligning modal action buttons to the right.

6 — align-items: Alignment Along the Cross Axis

align-items controls how flex items are positioned along the cross axis (perpendicular to the main axis). In a row container, this means vertical alignment. This single property replaces years of vertical-centering hacks.

Live demo — align-items (items have different natural heights)
A
short
B
tall
C
medium
align-items: stretch;
ValueBehaviour
stretch defaultItems stretch to fill the container's cross-axis size. All items match the height (or width) of the tallest sibling.
flex-startItems align to the cross-axis start (top, in a row). Items keep their natural size.
flex-endItems align to the cross-axis end (bottom, in a row).
centerItems centred on the cross axis. The classic "vertically centre anything" Flexbox trick.
baselineItems aligned so their text baselines line up. Useful when mixing different font sizes.
🎨 CSS — Perfectly centred content in two lines
.hero { display: flex; justify-content: center; /* centre on main axis (horizontal) */ align-items: center; /* centre on cross axis (vertical) */ min-height: 100vh; }
Before Flexbox, perfectly centring an element required absolute positioning with a negative-margin hack, or JavaScript calculations. Now it is two property declarations.

7 — flex-wrap: Handling Overflow

By default, flex items refuse to wrap — they squeeze onto a single line even if it means shrinking below their natural size. flex-wrap changes this behaviour.

Live demo — flex-wrap (7 items, fixed container width)
One
Two
Three
Four
Five
Six
Seven
flex-wrap: nowrap;
nowrap
★ default
All items on one line. Items shrink if needed, or overflow if flex-shrink: 0.
wrap
Items that do not fit wrap to new lines, stacking top-to-bottom.
wrap-reverse
Same as wrap but new lines appear above the first line (bottom-to-top stacking).
💡 The responsive card pattern. flex-wrap: wrap combined with flex: 1 1 250px on items creates a self-wrapping responsive grid — items sit side by side when there is room and drop to their own rows on small screens, with no media queries needed for the basic behaviour.

8 — gap: Space Between Items

gap adds consistent space between flex items — but never at the outer edges of the container. This makes it far cleaner than adding margins to individual items.

🎨 CSS — gap property
.container { display: flex; gap: 20px; /* same row and column gap */ gap: 16px 32px; /* row-gap column-gap */ row-gap: 16px; /* gap between wrapped rows */ column-gap: 32px; /* gap between items in a row */ }
gap only creates space between items. Adding padding to the container handles the space between items and the container's edges — the two properties work together, not against each other.
⚠️ Avoid margin-right hacks. A common older pattern is adding margin-right to every flex item, then using :last-child { margin-right: 0 } to remove the trailing gap. gap does this automatically — always prefer it.

9 — The flex Property: Grow, Shrink, Basis

The flex property is set on items (not the container) and controls how each item sizes itself relative to available space. It is shorthand for three sub-properties:

🎨 CSS — The flex shorthand
.item { flex: 1 1 auto; /* ↑ ↑ ↑ │ │ └── flex-basis: starting size before grow/shrink is applied │ └─────── flex-shrink: 1 = can shrink, 0 = never shrink └──────────── flex-grow: 0 = won't grow, 1+ = will grow */ } /* Common shorthand values: */ .item { flex: 1; } /* grow equally, can shrink, basis: 0 */ .item { flex: 0 0 auto; } /* natural size, no growing, no shrinking */ .item { flex: 0 0 240px; } /* fixed 240px sidebar that never resizes */ .item { flex: 1 1 200px; } /* responsive: start at 200px, wrap and grow */

flex-grow: claiming extra space

flex-grow is a unitless ratio. If one item has flex-grow: 3 and two others have flex-grow: 1, the first item claims three parts of the free space while each of the others claims one part. An item with flex-grow: 0 (the default) does not grow at all.

Live demo — flex-grow
A
B
C
flex: 1; /* all three items */

flex-shrink: giving up space

flex-shrink is the mirror of flex-grow. When items overflow their container, items with a higher shrink value give up proportionally more space. The default is 1 — all items shrink equally. Setting flex-shrink: 0 prevents an item from ever shrinking below its flex-basis — essential for fixed-width sidebars.

flex-basis: the starting size

flex-basis is the item's hypothetical size before growing or shrinking is applied. It overrides width in a row container, or height in a column container. Use auto to use the item's natural content size, 0 to start from nothing and grow proportionally from scratch, or a fixed value like 300px.

The four flex values you will write most often:
  • flex: 1 — equal-width columns filling available space
  • flex: 0 0 auto — item stays its natural size; does not grow or shrink
  • flex: 0 0 240px — fixed-width sidebar
  • flex: 1 1 200px — self-wrapping responsive item; starts at 200px, grows and wraps as needed

10 — align-self: Per-Item Alignment Override

align-self is set on an individual flex item and overrides the container's align-items value for that item only. It accepts the same values: auto (inherit from container — the default), stretch, flex-start, flex-end, center, baseline.

🎨 CSS — align-self on one item
.container { display: flex; align-items: center; /* all items vertically centred */ height: 100px; } .container .badge { align-self: flex-end; /* this one sits at the bottom */ }
A typical use case: a navbar where most items are vertically centred, but a notification badge or a "New!" chip is pinned to the bottom of the bar to visually stand out.

11 — Practical Patterns

Navigation bar: logo left, links right

🎨 CSS
.navbar { display: flex; justify-content: space-between; /* logo left, links right */ align-items: center; padding: 0 24px; height: 60px; } .nav-links { display: flex; /* nested flex for the link group */ gap: 24px; align-items: center; }

Equal-height card row with bottom-pinned buttons

🎨 CSS
.card-row { display: flex; gap: 20px; /* align-items defaults to stretch — all cards equal height */ } .card { flex: 1; display: flex; flex-direction: column; padding: 20px; } .card .btn { margin-top: auto; /* pushes button to card bottom regardless of content */ }
margin-top: auto on the last item in a column flex container claims all remaining space above itself, effectively pinning it to the bottom. This is the standard "sticky card button" pattern.

Fixed sidebar + flexible main

🎨 CSS
.page { display: flex; min-height: 100vh; } .sidebar { flex: 0 0 240px; /* fixed — never grows or shrinks */ } .main { flex: 1; /* claims all remaining width */ }

12 — Quick Reference

Properties set on the flex container

PropertyDefaultWhat it controls
display: flexActivates flex layout; makes direct children flex items
flex-directionrowDirection of the main axis (row / column)
justify-contentflex-startHow items and free space are distributed along the main axis
align-itemsstretchHow items are aligned on the cross axis
flex-wrapnowrapWhether items wrap to new lines when they overflow
gap0Space between items (not at container edges)
align-contentnormalDistribution of space between wrapped rows or columns

Properties set on flex items

PropertyDefaultWhat it controls
flex-grow0Proportion of extra space the item claims (0 = no growth)
flex-shrink1How much the item gives up when space is tight (0 = never shrink)
flex-basisautoStarting size before grow/shrink is applied
flex0 1 autoShorthand for flex-grow / flex-shrink / flex-basis
align-selfautoPer-item override of the container's align-items
order0Visual order within the flex line (does not affect DOM order)

✏️ Exercises

Build each layout in a fresh HTML file and open it in your browser. Attempt the task before looking at the solution — the process of working it out is where the learning happens.

Exercise 1
Create a horizontal navigation bar: a site logo on the left and three links (Home, About, Contact) on the right. The logo and links should be vertically centred. The bar should have a dark background and a height of 60px. Use Flexbox only — no floats, no absolute positioning.
Hint: justify-content: space-between pushes the logo and link group to opposite ends. Wrap the links in a <div> and make that a nested flex container with gap.
HTML
<nav class="navbar"> <div class="logo">MySite</div> <div class="nav-links"> <a href="#">Home</a> <a href="#">About</a> <a href="#">Contact</a> </div> </nav>
CSS
.navbar { display: flex; justify-content: space-between; align-items: center; height: 60px; padding: 0 24px; background: #1a1d27; } .logo { font-weight: 700; color: #4f8ef7; } .nav-links { display: flex; gap: 24px; align-items: center; } .nav-links a { color: #e2e4ec; text-decoration: none; }
Exercise 2
Build a row of three feature cards. Each card should have a title, a short paragraph of text (make one card have much more text than the others), and a "Learn more" button at the bottom. All cards should be equal height, and the button should always sit at the very bottom of its card regardless of how much text the card contains.
Hint: make the row a flex container and each card flex: 1. Make each card itself a column flex container. Give the paragraph flex: 1 so it grows, or use margin-top: auto on the button.
CSS
.card-row { display: flex; gap: 20px; } .card { flex: 1; display: flex; flex-direction: column; padding: 20px; background: #1a1d27; border-radius: 8px; } .card p { flex: 1; /* paragraph grows, pushing button down */ color: #a0a4b8; font-size: 0.9rem; } .card .btn { padding: 8px 16px; background: #4f8ef7; color: white; border: none; border-radius: 4px; cursor: pointer; align-self: flex-start; /* don't stretch button to full card width */ }
Exercise 3
Create a sidebar layout with a fixed 240px sidebar on the left and a flexible main content area filling the rest of the viewport. Both should stretch to at least full viewport height. The sidebar should never grow wider or shrink narrower than 240px.
Hint: flex: 0 0 240px locks the sidebar at exactly 240px. flex: 1 on the main area claims all remaining width. Set min-height: 100vh on the wrapper.
CSS
.page { display: flex; min-height: 100vh; } .sidebar { flex: 0 0 240px; background: #12141e; padding: 24px 16px; } .main { flex: 1; padding: 24px; }
Exercise 4
Build a responsive tag cloud. Create 10 pill-shaped tags in a container. On a wide screen they sit in a row; when there is not enough room they should wrap to new lines with consistent gaps. Tags should never grow — they should only be as wide as their label text. Wrap should use gap, not margins.
Hint: flex-wrap: wrap on the container, gap: 8px for spacing. Do not set flex-grow on the tags — let them stay at their natural content width.
HTML
<div class="tags"> <span class="tag">CSS</span> <span class="tag">Flexbox</span> <span class="tag">Responsive</span> <span class="tag">Layout</span> <span class="tag">Web Design</span> <span class="tag">Frontend</span> <span class="tag">HTML</span> <span class="tag">Beginner</span> <span class="tag">Tutorial</span> <span class="tag">Open Source</span> </div>
CSS
.tags { display: flex; flex-wrap: wrap; gap: 8px; } .tag { padding: 4px 14px; background: #1a2a4a; color: #7ab8ff; border: 1px solid #4f8ef7; border-radius: 999px; font-family: system-ui, sans-serif; font-size: 0.85rem; white-space: nowrap; }