Today we'll be looking at a common UI pattern:
Making the active item in a navigation bar or menu have a
font-weight: bold
while all other items have a font-weight: normal
.
<style>.nav-item {font-weight: normal;}.nav-item.is-active {font-weight: bold;}</style><nav><a class="nav-item is-active">Home</a><a class="nav-item">Projects</a><a class="nav-item">Blog</a></nav>
It's pretty straight-forward but a simple implementation like this comes with a little smudge of dirt:
When the nav-items are displayed in a row, the layout will jump slightly whenever the active item changes. Pay attention to the "Projects" link:
Argh... it just feels a little "off".
The problem is that by changing the font-weight
to bold
,
the element's size increases slightly, causing all the other items
to move a few pixels.
To get around these layout jumps, we have to render all nav-items
at their
maximum widths always.
Now the width of the nav-items
depends on their text content. To render them
at their maximum widths without JavaScript, we can simply render the text-content
with font-weight: bold
always but hide this text.
We then use an additional absolutely-positioned text-layer that switches from
font-weight: normal
to font-weight: bold
to display the actual text content
without affecting the flow of the layout.
<style>.nav-item {font-weight: normal;position: relative;}.nav-item.is-active {font-weight: bold;}.nav-item .label {position: absolute;top: 0;right: 0;left: 0;bottom: 0;width: 100%;height: 100%;display: inline-flex;align-items: center;justify-content: center;}</style><nav><a class="nav-item is-active"><!--Always render the text bold, but hide it visually. Using`visibility: hidden` will also hide it for assistive technology.This causes the `nav-item` to take up the width that it hasin its active state.--><span style="visibility: hidden; font-weight: bold;">Home</span><!--Render a visible label, using absolute positioning and flexto position it in the center of the nav-item element.This label will change `font-weight` depending on itsactive-state but won't affect layout because it's`position: absolute;` It will also never overflow becausethe `nav-item` container already has the largest possiblewidth.--><span class="label">Home</span></a><a class="nav-item"><span style="visibility: hidden; font-weight: bold;">Projects</span><span class="label">Projects</span></a><a class="nav-item"><span style="visibility: hidden; font-weight: bold;">Blog</span><span class="label">Blog</span></a></nav>
It's a little more markup but the good thing is that it requires no JavaScript and does not cause any accessibility issues.
Check out the result:
Isn't that a way calmer experience?
Little tweaks like this don't take a lot of extra time to implement but they really add up to a better user experience overall.
I hope you found this post helpful! You can find a working demo and code on CodePen.
Hi, I’m Max! I'm a fullstack JavaScript developer living in Berlin.
When I’m not working on one of my personal projects, writing blog posts or making YouTube videos, I help my clients bring their ideas to life as a freelance web developer.
If you need help on a project, please reach out and let's work together.
To stay updated with new blog posts, follow me on Twitter or subscribe to my RSS feed.