Python · SQL · Web Dev · Java · AI/ML tracks launching soon — your one platform for all of IT
Intermediate+250 XP

Flexbox — The Complete Guide

flex-direction and the main-axis vs cross-axis mental model, justify-content, align-items, align-content, flex-wrap, and flex-grow/flex-shrink/flex-basis worked through with real numeric examples.

50 min August 2026
// Part 01 — The Mental Model

The Main Axis and Cross Axis — The Single Most Important Idea in Flexbox

Everything in Flexbox — every property covered in this module — only makes sense once one idea is genuinely internalized: turning an element into a flex container creates two axes, and every alignment property in Flexbox refers to one of those two axes, never to literal "horizontal" or "vertical." Get this backwards and every Flexbox property will feel like it is behaving randomly; get it right and the entire spec becomes predictable.

Turning any element into a flex container
.container {
  display: flex;
  /* every direct child of .container is now a "flex item" */
}

The main axis is the direction items are laid out along — controlled entirely by flex-direction (Part 02). The cross axis is always exactly perpendicular to the main axis. This is the entire trick: flex-direction does not just change which way items flow, it redefines what "main" and "cross" mean for every other Flexbox property in the container.

The same two properties, meaning completely different things depending on flex-direction
.row {
  display: flex;
  flex-direction: row;         /* main axis = horizontal, cross axis = vertical */
  justify-content: center;      /* centers items HORIZONTALLY */
  align-items: center;           /* centers items VERTICALLY */
}

.column {
  display: flex;
  flex-direction: column;      /* main axis = vertical, cross axis = horizontal */
  justify-content: center;      /* centers items VERTICALLY — same property, different axis! */
  align-items: center;           /* centers items HORIZONTALLY — same property, different axis! */
}
⚠️ Important
This is the exact source of "I swapped justify-content and align-items and it fixed my layout" confusion. Neither property is ever really "horizontal" or "vertical" — one is always the main-axis property (justify-content) and one is always the cross-axis property (align-items/align-content). Once flex-direction changes, which physical direction each property affects flips with it. Memorize the rule as "justify = along the main axis, align = across the cross axis" rather than "justify = horizontal, align = vertical," and the confusion disappears permanently.
// Part 02 — flex-direction

flex-direction — Choosing the Main Axis

All four values
.a { flex-direction: row; }            /* default — main axis left to right */
.b { flex-direction: row-reverse; }     /* main axis right to left */
.c { flex-direction: column; }          /* main axis top to bottom */
.d { flex-direction: column-reverse; }  /* main axis bottom to top */

row is the default — if you never set flex-direction, a flex container lays its children out left-to-right (in a left-to-right language), with the main axis horizontal. The reverse variants flip the order items appear in visually, without changing their order in the actual HTML/DOM — an important distinction for accessibility, since screen readers and keyboard tab order still follow DOM order, not the visual reversed order.

row-reverse — visual order flips, DOM order and tab order do not
<div style="display: flex; flex-direction: row-reverse;">
  <button>First</button>
  <button>Second</button>
  <button>Third</button>
</div>

<!--
Visually renders: Third, Second, First
But Tab still moves focus First -> Second -> Third, because keyboard
navigation follows DOM order, not visual/painted order. A sighted
mouse user and a keyboard user can experience a genuinely different,
inconsistent sequence — this is a real accessibility consideration,
not a theoretical one.
-->
🎯 Pro Tip
Because of that exact keyboard-order mismatch, row-reverse and column-reverse are used far more cautiously in production interfaces than row/column — reach for them for purely decorative reordering, and verify with an actual keyboard (not just visually) whenever the reversed content is interactive.
// Part 03 — justify-content

justify-content — Distributing Space Along the Main Axis

justify-content controls how extra space is distributed along the main axis, once the flex items themselves have already taken up whatever room they need. It only has a visible effect when the items do not already fill the container completely.

All six common values, with flex-direction: row
.a { justify-content: flex-start; }     /* default — items packed at the start */
.b { justify-content: flex-end; }       /* items packed at the end */
.c { justify-content: center; }          /* items packed in the middle, equal space each side */
.d { justify-content: space-between; }    /* first item at start, last at end, EQUAL gaps between (none at the outer edges) */
.e { justify-content: space-around; }      /* equal space AROUND each item — edge gaps are HALF the between-item gaps */
.f { justify-content: space-evenly; }       /* every gap — including the two outer edges — is exactly equal */

The distinction between space-between, space-around, and space-evenly is a genuinely common point of confusion worth being precise about: with three items in a container, space-between creates two gaps (only between items, none at the edges); space-around creates four gaps, but the two edge gaps are each half the size of the two between-item gaps; space-evenly creates four gaps that are all exactly equal, including the edges.

// Part 04 — align-items and align-content

align-items and align-content — Two Different Cross-Axis Properties

align-items controls how items are positioned across the cross axis within a single line — the direct cross-axis equivalent of justify-content, but for one row (or column) of items.

align-items — the common values, with flex-direction: row
.a { align-items: stretch; }       /* default — items stretch to fill the container's cross-axis size */
.b { align-items: flex-start; }     /* items align to the top of the cross axis */
.c { align-items: flex-end; }        /* items align to the bottom */
.d { align-items: center; }           /* items centered vertically */
.e { align-items: baseline; }          /* items aligned by their text baseline — useful when items have different font sizes */
💡 Note
align-items: stretch is the default, and it genuinely surprises people the first time — flex items with no explicit height, inside a row-direction flex container, automatically stretch to match the tallest sibling's height unless align-items is set to something else. This is precisely the mechanism behind the "equal-height cards" pattern covered fully in the next module, and it happens with zero extra CSS beyond display: flex itself.

align-content — only relevant once items wrap onto multiple lines

align-content is easy to confuse with align-items, but it solves a different problem: it controls how entire lines of wrapped flex items are distributed across the cross axis relative to each other — it has zero visible effect with a single line of items (the overwhelmingly common case, e.g. flex-wrap: nowrap, the default). It only does anything once flex-wrap: wrap (Part 05) produces multiple lines and there is extra space in the cross-axis direction to distribute between them.

align-content — takes effect only with multiple wrapped lines
.gallery {
  display: flex;
  flex-wrap: wrap;
  height: 600px;            /* taller than the wrapped content needs, so there IS extra cross-axis space */
  align-content: space-between;  /* spreads the WRAPPED LINES apart from each other */
  /* align-items still controls alignment WITHIN each individual line */
}
// Part 05 — flex-wrap

flex-wrap — Allowing Items to Flow Onto New Lines

By default, every flex item is forced onto a single line (flex-wrap: nowrap), shrinking to fit if necessary (governed by flex-shrink, Part 07) rather than ever wrapping. flex-wrap: wrap allows items to flow onto additional lines once they no longer fit on the current one.

nowrap (default) vs wrap
.a {
  display: flex;
  flex-wrap: nowrap;   /* default — items squeeze/shrink to stay on one line, however cramped */
}

.b {
  display: flex;
  flex-wrap: wrap;   /* items that no longer fit flow onto a new line instead of shrinking indefinitely */
}

.c {
  display: flex;
  flex-wrap: wrap-reverse;   /* wraps, but new lines stack ABOVE the first line instead of below */
}
⚠️ Important
flex-wrap: nowrap is exactly why flex items sometimes shrink to an uncomfortably narrow, unreadable width instead of wrapping onto a new line as you might expect. With the default nowrap, items keep shrinking (per their flex-shrink value) to remain on one row no matter how narrow the container gets — this is very often not what a responsive layout actually wants, and explicitly adding flex-wrap: wrap is one of the single most common fixes applied when a Flexbox row breaks on narrow/mobile viewports.
// Part 06 — flex-grow

flex-grow — Distributing Extra Space, Worked Numerically

flex-grow controls how leftover space along the main axis is distributed once every item has already taken its base size. Its default is 0 — meaning by default, extra space is not distributed to items at all, it simply sits unused (governed by justify-content instead). The number is not a percentage or a pixel value — it is a ratio, and understanding it requires actually doing the arithmetic once.

Three items, three different flex-grow values — the exact math
.container {
  display: flex;
  width: 900px;
}
.item-a { flex-grow: 1; width: 100px; }  /* base width before growing */
.item-b { flex-grow: 2; width: 100px; }
.item-c { flex-grow: 1; width: 100px; }

/*
Total base width used: 100 + 100 + 100 = 300px
Leftover space to distribute: 900 - 300 = 600px

The three flex-grow values (1, 2, 1) sum to 4 "shares."
Each share of the leftover 600px is worth: 600 / 4 = 150px

item-a gets 1 share:  100px + (1 × 150px) = 250px final width
item-b gets 2 shares: 100px + (2 × 150px) = 400px final width
item-c gets 1 share:  100px + (1 × 150px) = 250px final width

Final check: 250 + 400 + 250 = 900px — exactly fills the container.
item-b ends up exactly TWICE as wide as item-a and item-c combined
relative to their growth, because its flex-grow ratio is exactly double.
*/

The takeaway that matters: flex-grow values are only meaningful relative to each other, not as absolute numbers. flex-grow: 1 on every item produces identical results to flex-grow: 100 on every item — what matters is each item's share of the total across all items in that container, not the specific numbers chosen.

The common 'one item fills remaining space, others stay fixed' pattern
.toolbar {
  display: flex;
}
.toolbar-title { flex-grow: 0; }   /* stays exactly its natural size */
.toolbar-spacer { flex-grow: 1; }   /* absorbs 100% of the leftover space — pushes the next item to the far end */
.toolbar-actions { flex-grow: 0; }  /* stays exactly its natural size */

/* This exact three-item pattern — a fixed title, an invisible flex-grow: 1
   spacer <div>, and fixed trailing action buttons — is one of the single
   most common real-world Flexbox layouts, used constantly for toolbars,
   headers, and any "stuff on the left, stuff pinned to the right" row. */
// Part 07 — flex-shrink

flex-shrink — The Same Ratio Logic, Running in Reverse

flex-shrink is the mirror image of flex-grow: instead of distributing extra space, it distributes a deficit — how much each item gives up when the container is too narrow to fit every item at its base size. Its default is 1 (unlike flex-grow's default of 0) — meaning, by default, every flex item is willing to shrink.

Three items, base widths that overflow the container — the exact shrink math
.container {
  display: flex;
  width: 500px;
}
.item-a { flex-shrink: 1; width: 300px; }
.item-b { flex-shrink: 1; width: 300px; }
.item-c { flex-shrink: 1; width: 300px; }

/*
Total base width: 300 + 300 + 300 = 900px
Container is only 500px — an overflow of 400px that must be removed.

With EQUAL flex-shrink values (1, 1, 1) and equal base widths, the
400px deficit is split evenly: each item shrinks by 400 / 3 ≈ 133px

item-a: 300 - 133 = 167px
item-b: 300 - 133 = 167px
item-c: 300 - 133 = 167px  (rounding — actual browser math is more precise)
*/
⚠️ Important
The real flex-shrink formula also factors in each item's base size, not just its shrink value — the browser weighs shrink ratio BY base size (flex-shrink × flex-basis), so a larger item with the same flex-shrink value gives up proportionally more absolute pixels than a smaller one. The simplified equal-base-size example above is accurate specifically because all three items share the same 300px base width; with differing base widths, the exact split is not simply the shrink ratio alone. The practical takeaway that matters day to day: flex-shrink: 0 on a specific item (commonly an icon or a fixed-width sidebar) prevents it from ever shrinking below its base size, letting only the other, shrink-enabled items absorb the deficit.
flex-shrink: 0 protecting a fixed-width sidebar from shrinking
.layout {
  display: flex;
}
.sidebar {
  flex-shrink: 0;      /* never shrinks below its own width, no matter how narrow the container gets */
  width: 240px;
}
.main-content {
  flex-shrink: 1;       /* absorbs all of the shrinking */
  flex-grow: 1;           /* also expands to fill any leftover space */
}
// Part 08 — flex-basis and the flex Shorthand

flex-basis — The Starting Size Before Grow/Shrink Are Applied

flex-basis sets an item's starting main-axis size before flex-grow or flex-shrink are applied — conceptually, "what width would this item be if grow/shrink math had not run yet." Its default value is auto, which falls back to the item's own width (in a row container) or content size if no width is set at all.

The three properties combined via the flex shorthand — the form used almost everywhere in real code
.item {
  flex: 1 1 200px;
  /* flex-grow: 1   — willing to grow to fill space
     flex-shrink: 1 — willing to shrink (this is also the default)
     flex-basis: 200px — starting size before grow/shrink math runs */
}

/* The three most common shorthand patterns you will actually see: */
.equal-width-item { flex: 1; }          /* shorthand for flex: 1 1 0% — items split space perfectly equally, ignoring their content's natural size entirely */
.fixed-size-item   { flex: 0 0 200px; }  /* never grows, never shrinks, always exactly 200px regardless of container size */
.natural-then-grow { flex: 1 1 auto; }    /* starts at its natural content size, then grows/shrinks from there */
🎯 Pro Tip
flex: 1 (shorthand for flex: 1 1 0%) is subtly different from flex: 1 1 auto, and the difference matters constantly in real layouts. With a flex-basis of 0%, the item's own content size is entirely ignored as a starting point — every item with flex: 1 ends up genuinely equal-width, regardless of how much text each one contains. With flex: 1 1 auto, each item starts from its own natural content width and then grows from there — items with more content can end up visibly wider than items with less, even though all of them share the same flex-grow: 1. Reach for flex: 1 specifically when items must be perfectly equal regardless of content.
// Part 09 — Real World
💼 What This Looks Like at Work

A Pricing Table Bug at a Chicago SaaS Company

Scenario — B2B SaaS company, Chicago · Marketing page bug

The marketing team ships a three-tier pricing table — Starter, Pro, Enterprise — as a horizontal row of cards. The "Pro" plan name is short. The "Enterprise" plan card, added a week later, needs an extra line of description text explaining custom SSO and dedicated support. As soon as it ships, the cards visibly stop matching widths: the Enterprise card is noticeably narrower than the other two, even though all three share the same CSS class.

The original pricing table CSS
.pricing-row {
  display: flex;
  gap: 24px;
}
.pricing-card {
  flex: 1 1 auto;   /* the culprit */
  padding: 32px;
  border-radius: 12px;
  background: white;
}

What the engineer finds

Exactly the distinction from Part 08's callout: flex: 1 1 auto starts each card's width from its own natural content size before growing — and the Enterprise card's extra sentence of description text gives it a larger natural content width than Starter or Pro. All three cards share the same flex-grow: 1, so they grow by equal amounts of extra space from there, but that equal amount is added on top of different starting sizes, so the final widths never converge to equal.

The fix — a one-value change
.pricing-card {
  flex: 1;   /* shorthand for flex: 1 1 0% — ignores each card's own content
                width as a starting point entirely, so all three grow from
                an identical zero baseline and end up genuinely equal-width */
  padding: 32px;
  border-radius: 12px;
  background: white;
}

The cards become pixel-identical in width regardless of how much copy any individual plan needs, and the fix survives the inevitable next update where a fourth "Team" tier gets added with yet another different amount of text. This exact flex: 1 vs flex: 1 1 auto distinction is one of the most common real Flexbox debugging sessions — "these should be equal width and they are not, despite identical CSS classes."

// Part 10 — Misconceptions

Five Misconceptions About Flexbox

✕ ""justify-content always controls horizontal alignment and align-items always controls vertical alignment""
Both properties are relative to the flex container's axes, not to literal screen directions. justify-content always aligns along the MAIN axis and align-items always aligns across the CROSS axis — which of those is horizontal versus vertical depends entirely on flex-direction. With flex-direction: column, justify-content controls vertical positioning and align-items controls horizontal positioning — the exact reverse of the row default.
✕ ""flex-grow: 2 means an item will be twice as wide as the container""
flex-grow values are ratios relative only to the OTHER items' flex-grow values in the same container, applied only to the leftover space after every item's base size is accounted for — not to the container's total width. An item with flex-grow: 2 gets twice as much of the leftover space as a sibling with flex-grow: 1, not twice the container's total width.
✕ ""flex: 1 and flex: 1 1 auto are basically the same thing""
flex: 1 is shorthand for flex: 1 1 0%, which ignores each item's own content size as a starting point, producing genuinely equal widths regardless of content. flex: 1 1 auto starts each item from its own natural content width before growing, which means items with different amounts of content can end up different final widths even with identical flex-grow values — exactly the pricing-card bug in the Real World example above.
✕ ""align-content and align-items do the same thing""
align-items positions items across the cross axis WITHIN a single line, and works regardless of whether items wrap. align-content positions entire WRAPPED LINES relative to each other across the cross axis, and has zero visible effect unless flex-wrap: wrap is set and there are actually multiple lines with extra cross-axis space to distribute.
✕ ""Flexbox items automatically wrap onto a new line when they run out of horizontal room""
The default is flex-wrap: nowrap, meaning items instead shrink (per flex-shrink, default 1) to stay on a single line, however cramped — they do NOT wrap automatically. flex-wrap: wrap must be explicitly set for items to flow onto new lines instead of squeezing.
// Part 11 — Interview Prep

6 Interview Questions — With Complete Answers

Explain the main axis and cross axis, and why understanding them is the key to the rest of Flexbox.
The main axis is the direction flex items are laid out along, set by flex-direction; the cross axis is always exactly perpendicular to it. Every Flexbox alignment property is defined relative to one of these two axes rather than to literal horizontal/vertical — justify-content always works along the main axis, align-items/align-content always work across the cross axis. Since flex-direction can flip which physical direction is "main," the same properties end up controlling different visual directions depending on flex-direction — which is the root of most Flexbox confusion until this mental model clicks.
Walk through exactly how three flex items with flex-grow values of 1, 2, and 1 split up 300px of leftover space in their container.
The three flex-grow values sum to 4 total "shares." Each share is worth 300 / 4 = 75px. The item with flex-grow: 1 gets 1 share (75px extra each), and the item with flex-grow: 2 gets 2 shares (150px extra) — so it ends up with exactly twice as much added width as either of its siblings. flex-grow only distributes the leftover space after each item's base size (flex-basis) is already accounted for — it is not a percentage of the container's total width.
What is the difference between flex: 1 and flex: 1 1 auto, and when does the difference actually show up visually?
flex: 1 is shorthand for flex-grow: 1; flex-shrink: 1; flex-basis: 0%, meaning every item starts from a zero baseline before growing, so items with equal flex-grow end up genuinely equal width regardless of content. flex: 1 1 auto keeps flex-basis: auto, so each item starts from its own natural content size and grows from there — items with more content can end up visibly wider than items with less, even with identical flex-grow. The difference shows up specifically whenever items have unequal natural content sizes, like cards or buttons with varying text lengths.
Why might a row of flex items shrink to an uncomfortably narrow width on a small screen instead of wrapping onto multiple lines?
flex-wrap defaults to nowrap, so items are forced to shrink (per each item's flex-shrink, default 1) to remain on a single line rather than wrapping automatically. This is a common cause of cramped, overflowing, or unreadable rows on narrow viewports, and the standard fix is explicitly adding flex-wrap: wrap so items flow onto new lines once they no longer fit, instead of continuing to shrink indefinitely.
When does align-content have any visible effect, and how does it differ from align-items?
align-content only has a visible effect when flex-wrap: wrap (or wrap-reverse) is set AND the flex items actually wrap onto multiple lines, with extra space left over in the cross-axis direction — it controls how those wrapped lines are distributed relative to each other. align-items, by contrast, controls alignment across the cross axis within a single line and applies regardless of wrapping. With only one line of items (the common nowrap default), align-content has no visible effect at all.
Describe a real Flexbox layout you would build using flex-grow: 0 on some items and flex-grow: 1 on exactly one item.
A toolbar or header row: a fixed-width logo/title on the left (flex-grow: 0, stays its natural size), an invisible spacer div in the middle with flex-grow: 1 that absorbs all of the leftover horizontal space, and fixed-width action buttons on the right (flex-grow: 0). Because the spacer is the only item willing to grow, it consumes 100% of the leftover space, which has the visual effect of pushing the right-hand buttons all the way to the far edge of the container — one of the most common real-world uses of flex-grow.
// Common Mistakes

Flexbox Mistakes Beginners Make Constantly

Expecting items to wrap onto a new line automatically
Broken: a row of items squeezes into an uncomfortably narrow width on small screens instead of wrapping, because flex-wrap defaults to nowrap. Fixed: add flex-wrap: wrap; explicitly whenever items should flow onto new lines instead of shrinking indefinitely.
Using flex: 1 1 auto and being confused why equally-styled items end up different widths
Broken: three cards with identical flex: 1 1 auto CSS but different amounts of text end up visibly different widths, because auto lets each item's own content size act as its starting point. Fixed: use flex: 1 (shorthand for flex: 1 1 0%) whenever items should be genuinely equal-width regardless of their content.
Setting justify-content to try to control cross-axis alignment
Broken: setting justify-content: center on a flex-direction: row container expecting it to vertically center items — it only affects the main (horizontal) axis in a row container. Fixed: use align-items: center for cross-axis (vertical, in a row) alignment; justify-content only ever affects the main axis.
Assuming align-content will do something with a single line of items
Broken: setting align-content: space-between on a container with flex-wrap: nowrap (or wrap with content that never actually wraps) and seeing zero visible change. Fixed: align-content only affects multiple WRAPPED lines — for single-line alignment across the cross axis, align-items is the correct property.
Forgetting that flex-shrink: 0 is needed to protect a fixed-width element from shrinking
Broken: a fixed-width 240px sidebar (with the flex-shrink default of 1) visibly shrinks below its intended width whenever the container gets tight, since every item shrinks by default. Fixed: add flex-shrink: 0; to the sidebar so only the remaining, shrink-enabled items absorb any space deficit.
// Error Library

Rendering Bugs You Will Hit — And Exactly Why

Flex items that should be equal width render with visibly different widths, despite sharing the same CSS class
Cause: The items use flex: 1 1 auto (or separately declared flex-basis: auto), which lets each item's own content size act as its starting width before flex-grow is applied — items with more content end up wider even with identical flex-grow values.
Fix: Use flex: 1 (equivalent to flex: 1 1 0%) whenever items must be genuinely equal-width regardless of their individual content.
A flex row overflows its container horizontally on narrow screens instead of wrapping
Cause: flex-wrap defaults to nowrap — items shrink (per flex-shrink) to try to stay on one line rather than wrapping, and if their combined minimum content size still exceeds the container, they overflow instead.
Fix: Add flex-wrap: wrap; so items flow onto additional lines once they no longer fit on the current one.
A fixed-width sidebar or icon visibly shrinks smaller than its declared width
Cause: flex-shrink defaults to 1, meaning every flex item is willing to shrink below its base size whenever the container is too narrow to fit everything at full size.
Fix: Set flex-shrink: 0; on the element that must never shrink below its intended size, so the deficit is absorbed entirely by the remaining, shrink-enabled siblings.
justify-content: center (or align-items: center) appears to do nothing at all
Cause: Most commonly, justify-content is being used to try to control the CROSS axis (or align-items to control the MAIN axis) — the properties are swapped relative to the container's current flex-direction.
Fix: Confirm the container's flex-direction first: with row, justify-content is horizontal and align-items is vertical; with column, this is exactly reversed.
align-content: space-between (or any align-content value) produces no visible change
Cause: align-content only affects multiple WRAPPED lines of flex items with extra cross-axis space to distribute — it has zero effect with a single line, which is the outcome with the default flex-wrap: nowrap, or with wrap set but not enough items to actually create a second line.
Fix: Confirm flex-wrap: wrap is set and that items genuinely wrap onto more than one line; for single-line cross-axis alignment, use align-items instead.

🎯 Key Takeaways

  • display: flex creates a main axis and a cross axis. flex-direction chooses which physical direction is the main axis — every other Flexbox property is defined relative to these two axes, not to literal horizontal/vertical.
  • justify-content distributes space along the MAIN axis; align-items aligns items across the CROSS axis within one line; align-content distributes multiple WRAPPED LINES across the cross axis and does nothing with a single line.
  • flex-grow distributes leftover space as a RATIO relative to sibling flex-grow values, only after each item's base size is accounted for — it is not a percentage of the container.
  • flex-shrink (default 1) lets items shrink below their base size when the container is too tight; flex-shrink: 0 protects a specific item (like a sidebar or icon) from ever shrinking.
  • flex-basis sets an item's starting size before grow/shrink math runs — flex: 1 (basis 0%) ignores content size entirely for genuinely equal widths; flex: 1 1 auto starts from each item's own natural content size instead.
  • flex-wrap defaults to nowrap — items shrink to stay on one line rather than wrapping automatically. flex-wrap: wrap must be set explicitly for items to flow onto new lines.
  • row-reverse and column-reverse only flip the VISUAL order — DOM order and keyboard tab order stay unchanged, a real accessibility consideration for interactive content.

What comes next

Module 24 takes every property from this module and applies it to real UI patterns — a responsive navbar, an equal-height card grid, the single-container centering reflex, a sticky footer layout, and the gap property.

Module 24 → Flexbox in Practice — Real Layouts
Share

Discussion

0

Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.

Continue with GitHub
Loading...