Skip to main content
Martin Hähnel

Creating A Navigation Menu For the Eleventy Base Blog

Changelog

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.


  1. 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. 😅 ↩︎