Creating A Navigation Menu For the Eleventy Base Blog
Changelog
- 2025-03-23 - Created this note
- 2025-03-24 - Fixed a missing word and added a link to the footnote. Added changelog section. Wrapped dark mode toggle in a
<li>
for accessibility reasons.
Note
Just a quick note on the fact that this blog now has a custom main navigation that can grow past the five or so items that Eleventy's Base Blog allows.[1]
The base.njk layout
Before:
...
<body>
<button id="dark-button" onclick="darkToggle()">Dark Mode</button>
<a href="#skip" class="visually-hidden">Skip to main content</a>
<header>
<a href="/" class="home-link">{{ metadata.title }}</a>
{#- Read more about `eleventy-navigation` at https://www.11ty.dev/docs/plugins/navigation/ #}
<nav>
<h2 class="visually-hidden">Top level navigation menu</h2>
<ul class="nav">
{%- for entry in collections.all | eleventyNavigation %}
<li class="nav-item"><a href="{{ entry.url }}"{% if entry.url == page.url %} aria-current="page"{% endif %}>{{ entry.title }}</a></li>
{%- endfor %}
</ul>
</nav>
</header>
<main id="skip">
...
After:
...
<body>
<a href="#skip" class="visually-hidden">Skip to main content</a>
<header>
<a href="/" class="home-link">{{ metadata.title }}</a>
<button
class="nav-toggle"
aria-expanded="false"
aria-controls="nav-menu"
onclick="
const nav = document.getElementById('nav-menu');
const expanded = nav.classList.toggle('open');
this.textContent = nav.classList.contains('open') ? '✕ Close' : '☰ Menu';
"
>
☰ Menu
</button>
</header>
<nav>
<h2 class="visually-hidden">Top level navigation menu</h2>
<ul id="nav-menu" class="nav">
{%- for entry in collections.all | eleventyNavigation %}
<li class="nav-item">
<a href="{{ entry.url }}"{% if entry.url == page.url %} aria-current="page"{% endif %}>
{{ entry.title }}
</a>
</li>
{%- endfor %}
<li>
<button id="dark-button" onclick="darkToggle()">Dark Mode</button>
</li>
</ul>
</nav>
<main id="skip">
...
So we split up <header>
and <nav>
. There is a new button with a class of nav-toggle that changes its textContent depending on if a menu is opened or not. The dark mode toggle moved into the nav items as the last item there.
The index.css
Before:
/* Nav */
.nav {
display: flex;
padding: 0;
margin: 0;
list-style: none;
}
.nav-item {
display: inline-block;
margin-right: 1em;
}
.nav-item a[href]:not(:hover) {
text-decoration: none;
}
.nav a[href][aria-current="page"] {
text-decoration: underline;
}
After:
/* Nav */
.nav {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
justify-content: center;
list-style: none;
border-bottom: 1px dashed var(--color-gray-20);
padding: 0.5rem 0 0.5rem;
margin: 0 0 1rem;
}
.nav-item {
margin: 0;
}
.nav-item a[href] {
display: inline-block;
padding: 0.5em 1em;
border-radius: 0.5em;
background: var(--nav-item-background);
text-decoration: none;
}
.nav-item a[href]:hover {
background: var(--nav-item-background-hover);
}
.nav a[href][aria-current="page"] {
text-decoration: underline;
}
.nav-toggle {
display: none;
}
@media (max-width: 640px) {
header {
position: relative;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding-bottom: 1.75rem;
}
.home-link {
margin-right: 0;
margin-top: 0.75rem;
}
.nav {
position: absolute;
top: 0;
left: 0;
right: 0;
transform: translateY(-100%);
transition: transform 0.3s ease-in-out;
background: var(--background-color);
padding: 1rem;
z-index: 1000;
flex-direction: column;
align-items: flex-start;
}
.nav.open {
transform: translateY(0);
}
.nav-toggle {
display: inline-block;
position: absolute;
top: 1rem;
right: 1rem;
z-index: 1100;
}
}
So there is quite a bit more CSS here. The main differences are that on mobile (devices smaller than 641px) the navigation menu (nav-toggle) is visible. We can see here that it is positioned absolute
so that it can double as the open/close button for the nav menu. The menu itself flies in from above on top of the normal layout, so things are not jumping around. It took me quite a bit of time to get this right.
On the desktop, things are now allowed to flow into the next row, which limits the width of the nav menu and I will feel less constrained adding new items to it.
Buttons - like the dark mode toggle - finally found a home at the end of the regular nav items. And if there is ever any need for more buttons, we can just append them here, too.
P.S.: Inspiration for this menu came from Jason's blog Cool As Heck.
Even though I was saying that I would try to not change the navigation, because using the default is just safer for somebody without a lot of visual skills. Cue maury lie detector meme, I guess. 😅 ↩︎
-
← Previous
DailyDogo 1213 🐶 -
Next →
DailyDogo 1214 🐶