Topic 7.5
Flexbox Patterns
In one line
Five layouts cover most of what Flexbox is used for: a navbar (auto margin), a media object (fixed image plus flex: 1; min-width: 0 text), a row of equal-height cards with buttons pinned to the bottom, a sticky footer (column + flex: 1 main) and the holy grail page layout.
Think of it like this
Recipes in a cookbook. Once you know how to boil, fry and bake, you don't invent dinner from scratch each night: you follow a handful of tried recipes and adjust them. Layout works the same way. With the tools from Topics 7.1–7.4 you can cook five patterns that appear on almost every website, and each one is only a few lines of CSS.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Pattern
- A well-known solution to a layout problem that comes up again and again.
- Navbar
- The bar of site links, usually at the top of a page, with a logo and maybe a button.
- Media object
- An image beside a block of text, like a comment with an avatar.
- Equal-height cards
- Cards in a row that all match the tallest one, so their edges line up.
- Sticky footer
- A footer that stays at the bottom of the window when the page is short, and after the content when it's long.
- `100dvh`
- 100% of the dynamic viewport height: the visible window height, updated as mobile browser bars appear and disappear.
- Holy grail layout
- A page with a header, a footer and three columns between them: nav, main and sidebar.
- Nesting
- Putting a flex container inside a flex item, so the item lays out its own children with Flexbox too.
Step by step
01Navbar: centre and split
The nav is a row, centred on the cross axis. The links list is pushed right by its auto margin. The list is a flex row too.
nav {
display: flex;
align-items: center;
gap: 16px;
}
nav ul {
display: flex;
gap: 12px;
margin: 0 0 0 auto; /* push the links to the right */
padding: 0;
list-style: none;
}02Media object: protect the image, free the text
The image never shrinks; the text takes the rest and can shrink below its content width.
.media { display: flex; align-items: flex-start; gap: 12px; }
.media img { flex: none; width: 48px; height: 48px; }
.media .body { flex: 1; min-width: 0; }03Card row: equal heights, aligned buttons
The outer row stretches every card to the same height. Each card is a column, and margin-top: auto on the button pushes it to the bottom.
.cards { display: flex; gap: 12px; }
.card { flex: 1; display: flex; flex-direction: column; }
.card button { margin-top: auto; }04Sticky footer: let main take the spare height
The body is a column at least one screen tall; main grows into the spare height.
body {
margin: 0;
min-height: 100vh;
min-height: 100dvh; /* newer browsers use this line */
display: flex;
flex-direction: column;
}
main { flex: 1; }05Holy grail: a column with a row inside
Combine the sticky footer with a three-column row in the middle, and stack the row on narrow screens.
.middle { flex: 1; display: flex; }
.middle nav, .middle aside { flex: 0 0 180px; }
.middle main { flex: 1; min-width: 0; }
@media (max-width: 600px) {
.middle { flex-direction: column; }
.middle nav, .middle aside { flex-basis: auto; }
}Try it yourself
- 1
Move the split
In the navbar, move
margin-left: autofrom theulto thebutton. Predict the layout: the logo and links stay together on the left and only the button goes right. Check that the first console line is stilltrue. - 2
Truncate the media text
In the media object, add
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;to.media p. Predict the text column's width. (Still 200, becausemin-width: 0lets it ignore the long line.) Then removemin-width: 0and predict again. - 3
Let the cards wrap
In the card row, add
flex-wrap: wrapto.cardsand change the cards toflex: 1 1 120px. Predict how many cards fit per line in a 270px row, and whether the heights stay equal. (Two fit; heights are equal only within each line.)
Code & diagrams
Make the preview wider: the logo stays left, the links and button stay right, and the gap between the two groups grows. All three groups are centred on the same horizontal line, even though the logo's font is bigger.
Preview
Console
Nothing logged yet.Expected console output
button at the right edge: true
logo at the left edge: true
logo and button share a middle: trueThe image keeps 48 x 48 even though the text is long; the text column gets 260 - 48 - 12 = 200px. Change `align-items` to the default `stretch`: an `<img>` stretched down would distort, which is why the pattern pins it to the top.
Preview
Console
Nothing logged yet.Expected console output
image: 48 x 48
text column: 200
image is taller than the text: falseThe middle card has much more text, yet all three cards are the same height (the row's `align-items: stretch`) and the buttons line up (`margin-top: auto` inside each column). Delete `margin-top: auto` and run again: the buttons follow their text.
Preview
Console
Nothing logged yet.Expected console output
equal heights: true
buttons line up: true
equal widths: trueThe page content is short, but `main` grows to fill the spare height, so the footer sits at the bottom of the window. Add many paragraphs to `main`: the page becomes taller than the window and the footer follows the content.
Preview
Console
Nothing logged yet.Expected console output
footer touches the bottom of the window: true
main grew taller than its text: trueIn a narrow preview the three middle parts stack; widen it past 400px and they become three columns. Grid with named areas (Topic 8.4) describes the same page in fewer lines.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use height instead of min-height
In the sticky footer example, change min-height: 100vh to height: 100vh and add twenty paragraphs to main.
Break #2
Card buttons don't line up
In the card row, remove display: flex; flex-direction: column; from .card but keep margin-top: auto on the button.
Myth vs fact
Myth
Equal-height columns need JavaScript or table layout.
Fact
Flex items in a row stretch to the tallest item by default (align-items: stretch).
Myth
A sticky footer needs position: fixed.
Fact
A fixed footer covers content and stays on screen. A sticky-footer layout only uses spare height: a column with flex: 1 on main.
Myth
Flexbox can't do page layouts; you need Grid.
Fact
Flexbox can build the holy grail. Grid is usually clearer for two-dimensional page layouts, and Flexbox is usually better inside components.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
100vhon mobile is the large viewport height (toolbars hidden), so content sized with it can sit under the browser's bottom bar.svh(small),lvh(large) anddvh(dynamic) units fix that; writemin-height: 100vhfirst andmin-height: 100dvhsecond so older browsers keep the first value. - ▸
For a media object holding an
<img>, setwidthandheightattributes on the image too: the browser reserves the space before the image loads, avoiding layout shift (CLS, a Core Web Vital). - ▸
Nested flex containers multiply layout passes: a stretched item with
flex-basis: automay be laid out twice (once to measure, once to stretch). In long feeds, keep nesting shallow and prefer definite sizes for repeated items. - ▸
Use logical properties in patterns that ship to many languages:
margin-inline-start: autoinstead ofmargin-left: auto, so a right-to-left navbar splits the right way without extra CSS.
Remember this
- 1
Navbar. A
<nav>is a flex row withalign-items: centerso the logo, links and button line up through their middles, andgapspaces them. To split the bar into a left group and a right group, give the first item of the right groupmargin-left: auto(ormargin-inline-start: auto, which also works right to left). The auto margin eats all the free space (Topic 7.2). The link list itself is a<ul>that is also a flex row: Flexbox containers nest freely. - 2
Media object. A picture (an avatar, a thumbnail, an icon) beside a block of text: comments, chat messages, notifications. The image gets
flex: none(orflex-shrink: 0) so it never gets squeezed, and the text getsflex: 1; min-width: 0so it takes the rest and wraps or truncates instead of pushing the row wider.align-items: flex-startkeeps the image at the top when the text is tall; the defaultstretchwould stretch a non-image element down the whole height. - 3
Card row. Cards sit side by side with
display: flexandgap, and the defaultalign-items: stretchmakes them equal height for free, whatever their text. Inside, each card is a column flex container, and the button at the bottom getsmargin-top: auto, so every button is pushed to the bottom edge and the buttons line up across cards. Addflex-wrap: wrapand a basis to let the cards wrap on small screens. - 4
Sticky footer. On a short page the footer should sit at the bottom of the window, not halfway up. Make
<body>a column flex container at least as tall as the viewport,min-height: 100vh(or100dvh, the dynamic viewport height, which follows mobile browser toolbars as they show and hide), and give<main>flex: 1. Main takes all the spare height, pushing the footer down. On a long page there's no spare height and the footer follows the content normally. Usemin-height, notheight, or long pages get cut off. - 5
Holy grail. The classic page: a header, a footer, and between them three columns, navigation on the left, main content in the middle and a sidebar on the right. With Flexbox: body is a column (as in the sticky footer), the middle section is a row, the side columns have fixed bases (
flex: 0 0 200px) and main hasflex: 1; min-width: 0. On narrow screens a media query switches the middle row toflex-direction: column. This works well, but whole-page layouts that need rows and columns to line up are exactly what Grid withgrid-template-areas(Topic 8.4) does more clearly. - 6
When to choose Flexbox for a pattern: when the layout is one direction (a row or a column), the number or size of items comes from the content, and you want items to share space. Choose Grid (Phase 8) when you design the rows and columns first and drop content into them. Many real layouts use both: Grid for the page, Flexbox inside each component.
Explain it without notes
How do you build a navbar with a logo on the left and links on the right using Flexbox?
What makes a media object robust when the text is very long?
How do you make cards in a row equal height with their buttons aligned at the bottom?
Explain how a Flexbox sticky footer works, and why min-height matters.
Practice
Build a notification row: a 32px icon, a message that takes the remaining space, and a "Dismiss" button at the far right, all centred vertically.
Make a page whose footer sits at the bottom of the window when the content is short.
Make three pricing cards equal height with their "Buy" buttons lined up at the bottom.
Trade-offs
- ↔
Flexbox patterns adapt to content with very little CSS, but rows that wrap can't keep columns aligned; switch to Grid when alignment across rows matters (a pricing table with matching feature rows, a photo grid).
- ↔
A holy grail in Flexbox needs nested containers and a media query; in Grid it's one container with named areas. Flexbox is still fine when the structure is simple and you already have the column wrapper.
Done when you can
I can build a navbar with a left and a right group.
I can build a media object that survives long text.
I can make equal-height cards with aligned buttons.
I can build a sticky footer and explain why it uses
min-height.I can build a holy grail layout that stacks on narrow screens.
I can say when a pattern is better done with Grid.