CSS Flexbox Deep Dive
A focused deep dive into CSS Flexbox: axes, sizing, alignment, growth, shrink, basis, and the small set of rules that explain most layouts.
What you'll learn
- ✓How main and cross axes drive every flex decision
- ✓What flex-grow, flex-shrink, and flex-basis really do
- ✓How alignment properties work together
- ✓Common layout patterns made simple
- ✓Pitfalls that confuse even experienced devs
Prerequisites
- •Comfortable with HTML and basic CSS
Flexbox feels intuitive once you internalize one fact: every decision Flexbox makes is in terms of two axes. Forget memorizing properties. If you can name the main axis and the cross axis at a glance, eighty percent of the confusion disappears.
What Flexbox is and why it exists
Before Flexbox the layout toolbox was floats, inline-block, and table tricks. None of them were designed for distributing space along a single dimension. Flexbox was created exactly for that job: arranging items in a row or a column, sizing them based on available space, and aligning them precisely.
The key idea is one-dimensional layout. Flexbox is great when you think “a row of things” or “a stack of things.” For two-dimensional grids, CSS Grid is the right tool.
Mental model
A flex container has two axes. The main axis runs in the direction of flex-direction. The cross axis runs perpendicular to it. Every property either places items along the main axis, aligns them on the cross axis, or controls how items grow and shrink to fill the main axis.
flex-direction: row
main axis ------------------------>
+-------+ +-------+ +-------+
^ | | | | | |
| | item | | item | | item |
cross | | | | | |
| +-------+ +-------+ +-------+
v
Switch flex-direction to column and the axes rotate. justify-content always works on the main axis. align-items always works on the cross axis. That single rule prevents most of the everyday confusion.
Hands-on example
Let us build a navigation bar where the logo sticks to the left, links sit in the middle, and a button stays on the right. This is the classic flex use case.
<nav class="nav">
<a class="logo">Brand</a>
<ul class="links">
<li><a href="#">Home</a></li>
<li><a href="#">Pricing</a></li>
<li><a href="#">Docs</a></li>
</ul>
<button class="cta">Sign in</button>
</nav>
.nav {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem 1.25rem;
}
.links {
display: flex;
gap: 1rem;
margin: 0 auto; /* pushes itself to the center */
}
The margin: 0 auto trick uses leftover space on both sides of the links to center them. Another option is justify-content: space-between and removing the auto margin, but you would then need a spacer. Same outcome, different reasoning.
container width: 600px
items: [A flex:1] [B flex:2] [C flex:1]
base width of each item: content size, say 100px
leftover space: 600 - 300 = 300px
A gets 1/4 of 300 = 75 extra -> final 175
B gets 2/4 of 300 = 150 extra -> final 250
C gets 1/4 of 300 = 75 extra -> final 175
When you write flex: 1 1 0 (the meaning of flex: 1), you tell the browser to grow from a basis of 0, sharing all available space according to grow factors. That is why items become equal width even if their content differs.
Common pitfalls
The biggest pitfall is treating flex-basis like width. Basis is the starting size before grow and shrink kick in. If you set basis: 200px and grow: 1 in a tight container, the item can still shrink below 200 because shrink defaults to 1. Setting flex: 0 0 200px locks it.
Another classic is overflow surprises. Flex items by default have min-width: auto on the main axis, which means they refuse to shrink below their content’s intrinsic size. A long unbreakable string can blow out your layout. The fix is min-width: 0 on the item.
A third pitfall is mixing justify-content and margins. They both fight for the same leftover space. Pick one strategy per axis and stick to it, otherwise small visual changes will become hard to reason about.
People also confuse align-items, align-self, and align-content. The first sets the default for all items, the second overrides it on a single item, and the third only applies when flex-wrap is in play and there are multiple lines.
Best practices
Use gap instead of margins between items. It composes well with wrapping and avoids the first-child or last-child margin dance.
Reach for flex-wrap when the layout has natural breakpoints, like card grids that should become a single column on mobile. Combined with min-width on items, it gives you responsive behavior without a single media query.
Keep flex shorthands explicit. flex: 1 is fine when you mean it, but if you only want growth without shrinking, write flex: 1 0 auto so the next person reading the code does not have to look up defaults.
When debugging, open dev tools and turn on the Flexbox overlay. Seeing the actual main and cross axes drawn on top of your layout removes a huge amount of guesswork.
Wrap-up
Flexbox rewards a small, sturdy mental model: two axes, three sizing inputs, two alignment families. Internalize that and every property name becomes self-explanatory. The next time a layout looks wrong, ask which axis the property targets and what the basis and shrink values are. The answer is almost always in those two questions.
Related articles
- HTML & CSS CSS Grid vs Flexbox: When to Use Each (with Examples)
Stop flipping a coin between Grid and Flexbox. Learn the mental model that picks the right layout tool every time, with practical CSS examples.
- HTML & CSS CSS Logical Properties Explained
Replace top, right, bottom, and left with logical block and inline properties that adapt to writing direction automatically.
- HTML & CSS CSS Positioning: Static, Relative, Absolute, Fixed, Sticky
A clear guide to the five CSS position values, how they interact with their containers, and when each one is the right choice.
- HTML & CSS CSS Subgrid Tutorial
Use CSS subgrid to align nested elements with their parent grid, perfect for card layouts, forms, and complex content where rows and columns must line up across components.