Arrange content with Flexbox
Outcome
Section titled “Outcome”You will use Flexbox to arrange the profile header, navigation links, and project entries. The layout will distribute available space, wrap when necessary, preserve the HTML source order, and remain usable at narrow widths and 200% browser zoom.
Why this matters
Section titled “Why this matters”Navigation bars, button groups, card rows, toolbars, and groups of controls often need a one-dimensional layout. Flexbox lets the browser resolve that layout from the available space instead of relying on fixed positions.
What you will practice
- Distinguish a flex container from its direct-child flex items.
- Identify the main axis and cross axis for row and column layouts.
- Use gap, justify-content, align-items, and flex-wrap for spacing, distribution, alignment, and line wrapping.
- Use flex-basis, flex-grow, and flex-shrink to control how items respond to available space.
- Preserve meaningful HTML source order and explain why visual reordering can reduce accessibility.
- Use browser DevTools to inspect a flex container, its axes, gaps, and item sizes.
What is new and what is reused
Section titled “What is new and what is reused”- New: Flex containers, flex items, the main and cross axes,
display: flex,flex-direction,gap,justify-content,align-items,flex-wrap,flex-basis,flex-grow,flex-shrink,order, and the Flexbox inspector in DevTools. - Reused: The
html-profileproject, semantic HTML, classes, the box model,border-boxsizing, fluid page widths, logical spacing, normal flow, browser reloading, and DevTools inspection.
Starting point
Before you start
- The html-profile folder from the preceding box-model lesson.
- index.html and styles.css at the same folder level.
- A profile header with a navigation list, a Projects section with at least two article elements, and the existing project-entry class.
- A page that already uses border-box sizing, a fluid width, a 48rem maximum width, and deliberate box-model spacing.
- VS Code and a current version of Edge, Chrome, or Firefox.
- Current state
- The page uses normal flow. The header content, navigation items, and project entries therefore appear as separate block boxes from top to bottom.
- First action
- Open html-profile/index.html and find the ul inside the page navigation. Add class="nav-list" to its opening tag.
- First checkpoint
- The page header and navigation links form flexible rows, and both rows wrap instead of leaving the viewport when space becomes limited.
- Help trigger
- Use the recovery note or ask for help if a Flexbox rule affects the wrong elements, content leaves the viewport, source order changes, or DevTools does not identify the selected element as a flex container.
Required result
Section titled “Required result”You have completed the lesson when:
- the page header is a flex container that distributes its direct children and can wrap them;
- the navigation list is a flex container with no list markers and a consistent gap between links;
- the Projects section contains a
project-listwrapper around the existing project articles; - the project list can show entries in a row when space permits and on separate lines when it does not;
- each project entry has an explicit basis, grow factor, and shrink factor;
- the visible order matches the meaningful HTML source order;
- all links and fragment destinations still work;
- all content remains visible at a narrow viewport and 200% browser zoom; and
- you can inspect one container in DevTools and explain its main axis, cross axis, gaps, wrapping, and item sizes.
Identify the container and its items
Section titled “Identify the container and its items”Flexbox starts with one element that becomes the flex container. Its direct children become flex items.
Container comparison
display creates the flex formatting context
The same three direct children use normal block flow first and Flexbox second.
display: block
Normal block flow places each child on a separate line.
display: flex
Flexbox places the direct children along the main axis.
The declaration display: flex changes layout behavior. It does not change the HTML meaning of the container or its children.
Only direct children become flex items. In this structure, the three article elements are flex items. Their nested headings and paragraphs are not flex items of project-list.
<div class="project-list"> <article class="project-entry"> <h3>Semantic profile</h3> <p>A profile page built with meaningful page regions.</p> </article> <article class="project-entry"> <h3>Reference journal</h3> <p>A journal with independent entries and related notes.</p> </article> <article class="project-entry"> <h3>Link collection</h3> <p>A small set of descriptive links to public work.</p> </article></div>Identify the main axis and cross axis
Section titled “Identify the main axis and cross axis”Every flex container has two axes:
- The main axis is the direction in which flex items are placed.
- The cross axis runs perpendicular to the main axis.
flex-direction sets the main axis. The default value is row.
Axis comparison
flex-direction sets the main axis
Changing the main axis also changes which direction justify-content controls.
flex-direction: row
- Main axis
- Left to right
- Cross axis
- Top to bottom
Items follow the horizontal main axis.
flex-direction: column
- Main axis
- Top to bottom
- Cross axis
- Left to right
Items follow the vertical main axis.
This lesson uses left-to-right English content. In that context, row starts at the left and runs to the right. Writing direction can change where a row starts, so think in terms of the main axis instead of treating row as a permanent synonym for left-to-right.
The axis model determines which alignment property to use:
justify-contentworks on the main axis.align-itemsworks on the cross axis.
Arrange the page header and navigation
Section titled “Arrange the page header and navigation”The header will use one flex row for its direct children. The navigation list inside it will use a second flex row for the links.
Add a class to the navigation list
Section titled “Add a class to the navigation list”In index.html, find the ul inside the page-level nav. Add the nav-list class and keep the existing list items and links unchanged:
<ul class="nav-list"> <li><a href="#about">About</a></li> <li><a href="#skills">Skills</a></li> <li><a href="#projects">Projects</a></li> <li><a href="#contact">Contact</a></li></ul>The list remains a semantic list. CSS changes its placement, not its meaning.
Add space between flex items with gap
Section titled “Add space between flex items with gap”gap creates consistent space between neighboring flex items. The gap belongs to the container and does not add space at its outer edges.
Spacing comparison
gap adds space between flex items
The item sizes stay the same. Only the space between neighboring items changes.
gap: 0
Neighboring item boundaries touch.
gap: 1rem
A 1rem gap separates each neighboring pair.
Use gap for spacing inside these flex containers. It avoids assigning different margins to the first and last items.
Distribute main-axis space with justify-content
Section titled “Distribute main-axis space with justify-content”justify-content controls how a container distributes free space on its main axis. Free space is the room left after the browser accounts for item sizes and gaps.
Main-axis comparison
justify-content distributes free space on the main axis
These row examples have the same item sizes and different main-axis distribution.
justify-content: flex-start
All free space remains after the item group.
justify-content: center
Free space is split before and after the item group.
justify-content: space-between
Free space is placed between items, with no added space at the outer edges.
If the items already use all available main-axis space, justify-content has no free space to distribute. A declaration can therefore be active without producing an obvious change.
Align items on the cross axis
Section titled “Align items on the cross axis”align-items sets the default cross-axis alignment for the flex items on their current flex line.
Cross-axis comparison
align-items sets the default cross-axis alignment
These row examples use a vertical cross axis and the same fixed container height.
align-items: stretch
Items without a fixed cross size stretch across the container.
align-items: flex-start
Items keep their natural height and align at the cross-axis start.
align-items: center
Items keep their natural height and sit midway along the cross axis.
The default stretch behavior only stretches an item when its cross size is automatic. A declared height in a row layout, for example, can prevent that item from stretching.
Allow a new flex line
Section titled “Allow a new flex line”flex-wrap decides whether a flex container must keep one line or can create additional lines.
Available-width comparison
flex-wrap decides whether items can form new lines
Both examples have three items whose preferred widths do not fit on one line.
flex-wrap: nowrap
The default keeps one line, so the items shrink to share the available width.
flex-wrap: wrap
Items move to additional flex lines instead of forcing every item onto one line.
The default is nowrap. With wrap, a later item moves to another line when its preferred main size does not fit. The HTML order remains unchanged.
Add the header and navigation rules
Section titled “Add the header and navigation rules”Add these rules after the existing page-width rule in styles.css:
/* Header layout */header { display: flex; flex-direction: row; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between;}
/* Navigation layout */.nav-list { display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 0; padding: 0; list-style: none;}The header’s direct children are the profile label and nav. justify-content: space-between places available main-axis space between them. align-items: center aligns them across the row. flex-wrap: wrap lets the navigation move below the label when the row cannot fit.
The nav-list declaration creates a separate flex container. Its direct li children become flex items. list-style: none removes the bullets, while the ul remains a list in the HTML.
- Save both files and reload the page.
- Confirm that the profile label and navigation share a row when enough width is available.
- Confirm that the navigation links form a row without bullets.
- Make the browser narrow and set browser zoom to 200%.
- Confirm that the header and navigation wrap, all links remain visible, and no content leaves the viewport.
- Select every navigation link and confirm that each link still reaches its matching section.
If it does not work
Section titled “If it does not work”- If the links stay in a vertical list, confirm that
class="nav-list"is on theuland that.nav-listincludesdisplay: flex. - If the complete page becomes a flex row, confirm that
display: flexis onheaderor.nav-list, not onbody. - If the header leaves the viewport, confirm that both flex containers include
flex-wrap: wrapand that no child has a new fixed width. - If bullets remain, confirm that
list-style: noneis in the.nav-listrule.
Checkpoint: The header and navigation respond to available width
- What now works
- The header distributes its label and navigation in a flexible row. The navigation links use a consistent gap, and both containers wrap at narrow widths without hiding links.
- Files changed
html-profile/index.html, html-profile/styles.css- What remains
- Group the project entries in a flex container and control how each entry grows and shrinks.
- Next action
- In index.html, wrap the project articles in a div with class project-list.
- If it does not work
- Inspect the header and navigation ul separately. Confirm that each selected element has display: flex and that its intended direct children appear as flex items.
Build a flexible project list
Section titled “Build a flexible project list”The Projects section contains a heading and several project articles. The heading should stay in normal flow above the entries, so the section itself should not become the flex container.
Add a layout wrapper around the articles
Section titled “Add a layout wrapper around the articles”In index.html, add one div around the existing project articles. Do not move the section heading into the wrapper.
<section id="projects"> <h2>Projects</h2> <div class="project-list"> <article class="project-entry"> <!-- Keep the existing project heading and paragraph here. --> </article> <article class="project-entry"> <!-- Keep the existing project heading and paragraph here. --> </article> </div></section>This is a structure excerpt. Use the comments as placement markers. Keep your existing project headings and paragraphs instead of replacing them with the comments.
The div has no added semantic meaning. It provides one container for the related entries because no more specific HTML element is needed. The article elements keep the meaning of each independent project entry.
Set the preferred item size with flex-basis
Section titled “Set the preferred item size with flex-basis”flex-basis sets a flex item’s preferred size on the main axis before the browser distributes free space. In a row, it supplies a preferred width. In a column, it supplies a preferred height.
Starting-size comparison
flex-basis sets an item’s preferred main size
The first item receives a larger starting size in the second panel.
All items: flex-basis: 4rem
Each item starts from the same main size.
Item 1: flex-basis: 8rem
Item 1 starts wider. Items 2 and 3 keep the 4rem basis.
The final size can differ from the basis because growing or shrinking can adjust the item after this starting point.
Let items use positive free space with flex-grow
Section titled “Let items use positive free space with flex-grow”flex-grow is a nonnegative factor. It controls how an item shares positive free space with other growing items on the same flex line.
Positive-space comparison
flex-grow distributes available positive free space
All items have the same 4rem basis. The second panel lets item 2 grow.
All items: flex-grow: 0
Unused main-axis space remains after the three items.
Item 2: flex-grow: 1
Item 2 receives the available positive free space.
A value of 0 prevents growth. If two items both use flex-grow: 1, they receive equal shares of the positive free space. This does not guarantee equal final widths when their bases or content constraints differ.
Let items reduce their size with flex-shrink
Section titled “Let items reduce their size with flex-shrink”flex-shrink is also a nonnegative factor. It controls how an item participates when the combined item sizes exceed the available main-axis space.
Negative-space comparison
flex-shrink distributes a size reduction when items do not fit
Each item starts at 8rem, which is wider than the available row when combined.
All items: flex-shrink: 1
All three items become narrower so they can share the line.
Item 2: flex-shrink: 0
Item 2 keeps its 8rem basis. Items 1 and 3 take more of the size reduction.
A value of 0 prevents an item from shrinking below its basis through the flex shrink calculation. Use that value only when the item must retain its main size and the container has another valid way to handle the missing space.
Add the project-list and item rules
Section titled “Add the project-list and item rules”Add this container rule to styles.css:
/* Project list layout */.project-list { display: flex; flex-wrap: wrap; gap: 1rem;}Then replace the existing .project-entry rule with this complete version:
/* Reusable project style and flex sizing */.project-entry { flex-basis: 16rem; flex-grow: 1; flex-shrink: 1; margin: 0; border: 2px solid #9aa9b8; background-color: #ffffff; padding: 1.25rem;}The 16rem basis gives each entry a readable preferred width. flex-grow: 1 lets entries share space that remains on a line. flex-shrink: 1 lets an entry become narrower when necessary. flex-wrap: wrap lets another entry move to a new line before all entries become too compressed.
The previous lesson used margin-block between project entries. This rule changes the margin to 0 because the container’s gap now owns that spacing.
- Save both files and reload the page.
- At a wide viewport, confirm that two project entries can share one row.
- Make the viewport narrower. Confirm that an entry moves to another line when both preferred widths no longer fit.
- At the narrowest useful width and 200% zoom, confirm that every project heading and paragraph remains visible.
- Confirm that the Projects heading stays above the project list instead of becoming one of its flex items.
If it does not work
Section titled “If it does not work”- If the Projects heading joins the row, move
display: flexfrom#projectsto.project-listand confirm that only the articles are inside the wrapper. - If the entries remain stacked at every width, inspect
.project-listand confirm that it matches the new wrapper and hasdisplay: flex. - If no gap appears, confirm that
.project-entrynow hasmargin: 0and.project-listhasgap: 1rem. - If content becomes clipped, remove any fixed
heightoroverflow: hiddendeclaration left from the preceding lesson’s temporary experiment.
Checkpoint: Project entries grow, shrink, and wrap
- What now works
- Project articles share a row when space permits, move to new lines when necessary, and keep all text visible. The Projects heading remains in normal flow above them.
- Files changed
html-profile/index.html, html-profile/styles.css- What remains
- Protect source order, inspect the final layout in DevTools, and complete the final verification.
- Next action
- Compare the HTML source order with the visible header, navigation, and project order.
- If it does not work
- Select div.project-list in DevTools. Confirm that it is the flex container and that each article.project-entry is a direct-child flex item.
Keep source order and visual order consistent
Section titled “Keep source order and visual order consistent”Flexbox can change visual placement without changing the DOM. The order property demonstrates why this distinction matters.
Accessibility comparison
order can separate visual order from source order
The DOM order stays 1, 2, 3 in both examples. Only the second visual changes placement.
No order change
Visual order and DOM order both remain 1, 2, 3.
Item 3 uses order: -1
Visual order becomes 3, 1, 2, while DOM order remains 1, 2, 3.
Browsers, assistive technologies, search tools, and keyboard navigation use the document structure. A visual order that conflicts with the source order can make the same page communicate different sequences to different users.
Do not add order to the required profile styles. Keep the profile label, navigation links, project entries, and contact content in a meaningful HTML order. Use wrapping to adapt placement without changing that sequence.
Inspect Flexbox in DevTools
Section titled “Inspect Flexbox in DevTools”Current browser DevTools can identify flex containers and show a layout overlay. The exact button and panel name depend on the browser version.
- Save and reload the page.
- Right-click one project entry and select Inspect.
- Move to its parent
div class="project-list"in the element tree. - Confirm that the Styles or Rules panel shows
display: flexas an active declaration. - Select the flex, Flexbox, Layout, or equivalent badge or panel for that container.
- Turn on the layout overlay if the browser provides one.
- Identify the row main axis, the vertical cross axis, the gap, each item boundary, and any wrapped line.
- Make the browser wider and narrower. Observe when the articles share a line and when they wrap.
- Select one
article class="project-entry"and inspect its activeflex-basis,flex-grow, andflex-shrinkvalues. - Change
flex-basistemporarily in DevTools. Observe the new layout, then reload the page to discard the temporary edit.
If it does not work
Section titled “If it does not work”- If DevTools does not show a Flexbox control, confirm that the selected element is
div.project-list, not one of its child articles. - If
display: flexis crossed out, inspect the competing rule and confirm that the selector and braces instyles.cssare correct. - If a property edit remains after reload, confirm that you changed it only in DevTools and did not save the same temporary value in
styles.css.
Checkpoint: The Flexbox inspector matches the rendered layout
- What now works
- DevTools identifies the project list as a flex container and the articles as flex items. You can point to the axes, gaps, wrapped lines, basis, grow factor, and shrink factor.
- Files changed
html-profile/index.html, html-profile/styles.css- What remains
- Complete the final checks and explain how available space produces the visible layout.
- Next action
- Complete the self-check against the saved source, the rendered page, and the DevTools evidence.
- If it does not work
- Start from div.project-list, verify its active display value, then inspect one container property or item property at a time.
Self-check
Complete these checks against the required result.
- Identify each flex container in the final page and name its direct-child flex items.
- Point to the main axis and cross axis of the row-based header and project-list containers.
- Explain what display: flex changes and what semantic information it does not change.
- Confirm that gap creates the visible space inside the navigation and project list without adding outer-edge spacing.
- Explain what free space justify-content distributes and why it can have no visible effect in a full container.
- Confirm that align-items centers the header items on the cross axis when they share a flex line.
- View the page at narrow and wide widths and explain when flex-wrap creates another line.
- Inspect one project entry and identify its flex-basis, flex-grow, and flex-shrink values.
- Confirm that the visible navigation and project order matches the HTML source order.
- Select every navigation link and confirm that all fragment destinations still work.
- View the page at 200% zoom and confirm that all text and links remain visible without unintended horizontal scrolling.
- Use the DevTools Flexbox overlay or layout panel to identify the project-list container, its item boundaries, gaps, and wrapped lines.
Practice the Flexbox properties with Flexbox Froggy
Section titled “Practice the Flexbox properties with Flexbox Froggy”Flexbox Froggy is an external practice game. It asks you to use Flexbox declarations to place frogs on matching lily pads. It does not edit your html-profile files and does not replace the browser and DevTools checks in this lesson.
Recommended target: Complete levels 1–19. Those levels practice justify-content, align-items, flex-direction, order, and flex-wrap.
Levels 16 and 17 introduce align-self. Read the game’s explanation and solve the levels, but do not add align-self to the required profile page. Stop after level 19. Levels 20–24 introduce flex-flow and align-content, which this lesson does not require.
Use the game’s Colorblind Mode if the frog and lily-pad colors are difficult to distinguish. If you get stuck, identify the main axis and cross axis first, then use the game’s built-in description of the relevant property.
How the browser resolves the flex layout
Section titled “How the browser resolves the flex layout”The browser first identifies the flex container and its direct-child flex items. flex-direction establishes the main axis. The browser uses each item’s basis and content constraints as a starting point, then accounts for gaps and available container space.
If positive free space remains, grow factors can distribute it. If the items need more space than the line provides, shrink factors can reduce their main sizes. Wrapping can create another flex line when the preferred sizes do not fit. The browser then applies main-axis distribution and cross-axis alignment to each line.
Every flex item still uses the box model. Its content, padding, border, and margin remain part of the size calculation. Flexbox changes how a container places its direct children; it does not replace the box model or the semantic HTML structure.
When to use Flexbox and when to keep normal flow
Section titled “When to use Flexbox and when to keep normal flow”Use Flexbox when a group needs a shared one-dimensional relationship, such as alignment, distribution, flexible item sizing, or wrapping.
Keep normal flow when content only needs to follow source order from top to bottom. The About paragraph, section headings, Current focus note, Contact content, and footer text do not need display: flex in this project. Their existing block flow already matches the intended reading order.
Record the Flexbox checkpoint
Section titled “Record the Flexbox checkpoint”After the required self-check passes, record the working state in the existing html-profile repository:
git statusgit diffgit add index.html styles.cssgit diff --stagedgit commit -m "Arrange profile content with Flexbox"git pushReload the private GitHub repository and confirm that the commit contains the intended wrappers, classes, and Flexbox rules.
Next step or safe stopping point
Section titled “Next step or safe stopping point”The required lesson is complete when the profile header, navigation, and project list respond to available space; the source and visual orders agree; all content remains available at narrow widths and 200% zoom; and you can explain the final layout with DevTools evidence.
Continue to Write reusable and predictable CSS to organize selectors, custom properties, and cascade behavior into a more maintainable stylesheet.
If you stop here, leave yourself this resume note: The Flexbox layout is working. Next, open the reusable CSS lesson and identify repeated values and selector relationships in styles.css.