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:
display: flex on this element. It controls the direction, alignment, and spacing of its children. Layout rules are declared here.flex property.2 — Creating a Flex Container
A single declaration transforms an element into a flex container:
/* 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.<nav class="navbar">
<a href="#">Home</a>
<a href="#">About</a>
<a href="#">Contact</a>
</nav>
.navbar {
display: flex;
gap: 24px;
padding: 12px 20px;
background: #1a1d27;
}
.navbar a {
color: #e2e4ec;
text-decoration: none;
}
<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. Items are placed along it. justify-content distributes space here.
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.
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).
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.
| Value | Behaviour |
|---|---|
flex-start default | Items packed at the start. All free space appears at the end. |
flex-end | Items packed at the end. Free space appears at the start. |
center | Items grouped in the centre. Equal free space at both ends. |
space-between | First item at start, last at end. Remaining space split equally between items. No space at edges. |
space-around | Equal space on each side of every item. Edge gaps are half the size of inner gaps. |
space-evenly | Identical gaps between all items and at both edges. |
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.
short
tall
medium
| Value | Behaviour |
|---|---|
stretch default | Items stretch to fill the container's cross-axis size. All items match the height (or width) of the tallest sibling. |
flex-start | Items align to the cross-axis start (top, in a row). Items keep their natural size. |
flex-end | Items align to the cross-axis end (bottom, in a row). |
center | Items centred on the cross axis. The classic "vertically centre anything" Flexbox trick. |
baseline | Items aligned so their text baselines line up. Useful when mixing different font sizes. |
.hero {
display: flex;
justify-content: center; /* centre on main axis (horizontal) */
align-items: center; /* centre on cross axis (vertical) */
min-height: 100vh;
}
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.
flex-shrink: 0.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.
.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.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:
.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.
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.
flex: 1— equal-width columns filling available spaceflex: 0 0 auto— item stays its natural size; does not grow or shrinkflex: 0 0 240px— fixed-width sidebarflex: 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.
.container {
display: flex;
align-items: center; /* all items vertically centred */
height: 100px;
}
.container .badge {
align-self: flex-end; /* this one sits at the bottom */
}
11 — Practical Patterns
Navigation bar: logo left, links right
.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
.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
.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
| Property | Default | What it controls |
|---|---|---|
display: flex | — | Activates flex layout; makes direct children flex items |
flex-direction | row | Direction of the main axis (row / column) |
justify-content | flex-start | How items and free space are distributed along the main axis |
align-items | stretch | How items are aligned on the cross axis |
flex-wrap | nowrap | Whether items wrap to new lines when they overflow |
gap | 0 | Space between items (not at container edges) |
align-content | normal | Distribution of space between wrapped rows or columns |
Properties set on flex items
| Property | Default | What it controls |
|---|---|---|
flex-grow | 0 | Proportion of extra space the item claims (0 = no growth) |
flex-shrink | 1 | How much the item gives up when space is tight (0 = never shrink) |
flex-basis | auto | Starting size before grow/shrink is applied |
flex | 0 1 auto | Shorthand for flex-grow / flex-shrink / flex-basis |
align-self | auto | Per-item override of the container's align-items |
order | 0 | Visual 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.
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.<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>
.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;
}
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..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 */
}
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..page {
display: flex;
min-height: 100vh;
}
.sidebar {
flex: 0 0 240px;
background: #12141e;
padding: 24px 16px;
}
.main {
flex: 1;
padding: 24px;
}
gap, not margins.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.<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>
.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;
}