A use case for margin in css flexbox
While working through Josh Comeau's CSS course (highly recommended - review coming soon), I came across a helpful technique to build simple layouts.
In this post, I'll walk through four different ways to align a basic header layout (the one using margin
is number 4).
Fair warning: this isn't exactly groundbreaking work! But it does help build an intuition that saves time on frontend work later on.
Read the "The Layout" sectionThe Layout
We're writing a header for a website. It has links to different sections, with the logo linking to the 'home page'.
Here's the HTML for our header, with navbar
a flex container:
<nav class="navbar">
<a href="#" class="navlink"><logo></a>
<a href="#" class="navlink">Products</a>
<a href="#" class="navlink">Pricing</a>
<a href="#" class="navlink">About</a>
</nav>
The goal is to push the logo to the left and align the navigation links to the right, so the header looks like this:
Let's go through a few different approaches and compare their merits.
Read the "1. flex: 1 on the logo" section1. flex: 1
on the logo
The idea: Apply flex: 1 to the logo, so it stretches and pushes the other items to the right.
.navlink:first-child {
flex: 1;
}
There's just one issue with this: the empty space created by flex: 1
is clickable, which feels awkward - especially on wider screens where the space is large.
Read the "2. A spacer component" section2. A spacer component
The idea: insert an empty element that grows to fill up available space.
We add an extra element in the HTML:
.spacer {
flex: 1;
}
<nav class="navbar">
<a href="#" class="navlink"><logo></a>
<div class="spacer"></div>
<a href="#" class="navlink">Products</a>
<a href="#" class="navlink">Pricing</a>
<a href="#" class="navlink">About</a>
</nav>
This avoids the clickable blank space issue, since the empty space is now part of the spacer component.
This works, but with an edge case when the flex container uses gap
: on smaller screens, the spacer shrinks to zero but the gap is still applied to both sides of the spacer.
This doubles the spacing and makes the layout overflow on screens where it should comfortably fit.
Read the "3. Nested flexbox" section3. Nested flexbox
The idea: Wrap the logo in a flex container and use a spacer element inside that container, while keeping the main navbar a separate flexbox.
The HTML has an extra wrapper element:
.logo-container {
display: flex;
flex: 1;
}
.spacer {
flex: 1;
}
<nav class="navbar">
<div class="logo-container">
<a href="#" class="navlink"><logo></a>
<div class="spacer"></div>
</div>
<a href="#" class="navlink">Products</a>
<a href="#" class="navlink">Pricing</a>
<a href="#" class="navlink">About</a>
</nav>
This approach works well and avoids the earlier issues. It does add an extra layer of nesting, but this generally isn't a big deal.
Read the "4. An alternative - margin-right: auto" section4. An alternative - margin-right: auto
Another option is to use margin-right: auto
on the logo. This makes the logo 'greedily' claim space and pushes the other navigation links to the end of the flexbox.
For more technical details, the flex spec explains the free space is distributed to auto margins after flex length calculations and before flex 'alignment' (they even use a similar example).
This is similar to the 'regular' behavior of margin: auto
(in block layout).
.navbar .navlink:first-child {
margin-right: auto;
}
This keeps the HTML to a bare minimum and is arguably the simplest solution.
This is also the 'helpful technique' that prompted this post.
Read the "Recap" sectionRecap
Here's a playground with all four options. Try resizing the preview to check behavior:
Read the "Building intuition - the cure to aimless flexbox shuffling" sectionBuilding intuition - the cure to aimless flexbox shuffling
Here's what I might have done in the past:
- Use
flex: 1
, realize the clickable space issue. - Replace it with a spacer component, run into gap problems on smaller screens.
- Decide I need two flexboxes, adjust existing styles as needed: success.
This trial-and-error process is not necessarily bad1 - it's part of learning. But it is also very time-consuming: I would often spend 20 minutes cycling through different approaches on some frontend component.
I don't think my process is unique. It seems like with CSS, many otherwise methodical developers resort to half-blind trial and error.
Breaking that pattern requires a better CSS intuition: the ability to predict layout behavior before writing code.
Working on small examples in a controlled setting is a great way to build such an intuition.
So that's why I'm blogging about four approaches to the layout of a header (yawn, I know) - to share how I approach improving at CSS, one playground at a time.
Read the "Footnotes" sectionFootnotes
-
If anything, I'd argue that being able to notice subtle issues is more important than happening upon a correct solution. ↩