Skip to content

Instantly share code, notes, and snippets.

@pdarragh
Last active September 30, 2023 10:43
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save pdarragh/c7ca120604c1a1d8b8de to your computer and use it in GitHub Desktop.
Save pdarragh/c7ca120604c1a1d8b8de to your computer and use it in GitHub Desktop.
Jekyll navigation bar with automatic highlighting.

Jekyll NavBar

In building a site powered by Jekyll and hosted by GitHub, I wanted the ability to highlight the current page's tab in the bar. I also wanted the bar to support second-level items (i.e. a dropdown), which proved somewhat tricky. This is the solution I arrived at after a few hours of fiddling around.

Construction

The contents of the navigation bar are contained in a data file located at _data/navigation.yml. This makes it accessible via the site-wide Liquid element {{ site.data.navigation}}. You can see the file for the formatting I used.

How it Works

This data is used in the navigation file, located at _includes/navigation.html (which is therefore able to be included in your templates via {% include navigation.html}).

Finding the current page in the list

The first section of the file has a loop that accomplishes two things:

  1. Does the current element of the loop have the exact same URL as the current page?
  2. If not, does the current element of the loop fit inside the current page's URL? (This is for nested pages.)

In the event of (1), the loop sets the current_page value and breaks. In the event of (2), the loop sets the current_page value but continues to loop. This effectively means the loop is searching for a best-fit value for the current page's URL.

Building the bar

Then the navigation bar is built. It loops over each entry and immediately checks if the current element is the same as the current_page value we found previously. If they are a match, a current variable is created that contains the class identifier to highlight the tab. If they are not a match the current variable must be set to null. This is to ensure you don't accidentally end up with a cascading highlighting effect. (Trust me, it's unwanted.)

Caveats

  • Any page that does not have an entry in the navigation.yml file will highlight a page with url: /.
  • My method is only built for a single sub-level. You'd have to adjust it for further sub-levels.
<!-- Belongs at: /_includes/navigation.html -->
<!-- This finds the current page so it can be highlighted. -->
{% for entry in site.data.navigation %}
{% capture fullurl %}{{ site.baseurl }}{{ entry.url }}{% endcapture %}
{% if fullurl == page.url %}
{% assign current_page = fullurl %}
{% break %}
{% elsif page.url contains fullurl %}
{% assign current_page = fullurl %}
{% endif %}
{% endfor %}
<!-- Then we build the nav bar. -->
<nav>
<ul>
{% for entry in site.data.navigation %}
{% if entry.url == current_page %}
{% assign current = ' class="current"' %}
{% else %}
<!-- We have to declare it 'null' to ensure it doesn't propagate. -->
{% assign current = null %}
{% endif %}
{% assign sublinks = entry.sublinks %}
{% if sublinks %}
<li{{ current }}>
<a href="{{ site.baseurl }}{{ entry.url }}">{{ entry.title }}</a>
<ul>
{% for sublink in sublinks %}
<li><a href="{{ site.baseurl }}{{ sublink.url }}">{{ sublink.title }}</a></li>
{% endfor %}
</ul>
</li>
{% else %}
<li{{ current }}><a href="{{ site.baseurl }}{{ entry.url }}">{{ entry.title }}</a></li>
{% endif %}
{% endfor %}
</ul>
</nav>
# Belongs at: /_data/navigation.yml
- title: Home
url: /
- title: Group of Things
url: /group/
sublinks:
- title: Sub-item 1
url: /group/si1
- title: Sub-item 2
url: /group/si2
- title: Sub-item 3
url: /group/si3
@catherineluse
Copy link

I'm using this in my GitHub pages site, and it generates the list of links just fine. However, it doesn't highlight the selected page. I tried adding classes to the css file named "current" and it still doesn't work. Do you know why it might not be using the right css for the current link?

@ongyx
Copy link

ongyx commented May 10, 2020

It might be a better idea to use inline CSS as the "style" attribute instead (for immediate highlight, no external CSS needed):

<!-- Belongs at: /_includes/navigation.html -->

<!-- This finds the current page so it can be highlighted. -->

{% for entry in site.data.navigation %}
{% capture fullurl %}{{ site.baseurl }}{{ entry.url }}{% endcapture %}
    {% if fullurl == page.url %}
        {% assign current_page = fullurl %}
        {% break %}
    {% elsif page.url contains fullurl %}
        {% assign current_page = fullurl %}
    {% endif %}
{% endfor %}

<!-- Then we build the nav bar. -->
<nav>
    <ul>
    {% for entry in site.data.navigation %}
        {% if entry.url == current_page %}
            <!-- uses yellow, you can change to any other hexadecimal colour code. -->
            {% assign current = ' style="background-color: #FFFFFF"' %}
        {% else %}
            <!-- We have to declare it 'null' to ensure it doesn't propagate. -->
            {% assign current = null %}
        {% endif %}
        {% assign sublinks = entry.sublinks %}
        {% if sublinks %}
        <li{{ current }}>
            <a href="{{ site.baseurl }}{{ entry.url }}">{{ entry.title }}</a>
            <ul>
                {% for sublink in sublinks %}
                <li><a href="{{ site.baseurl }}{{ sublink.url }}">{{ sublink.title }}</a></li>
                {% endfor %}
            </ul>
        </li>
        {% else %}
        <li{{ current }}><a href="{{ site.baseurl }}{{ entry.url }}">{{ entry.title }}</a></li>
        {% endif %}
    {% endfor %}
    </ul>
</nav>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment