#4022: navbar.html

site/_includes/navbar.html

Path: site/_includes/navbar.html · Lines: 151 · Type: Jekyll Liquid include template

Purpose: THE navigation bar include — the single most complex Liquid template in the site. Renders: sticky header, dual left/right nav from YAML data, nested dropdowns, mobile offcanvas toggle, SimpleJekyllSearch integration, logo handling, active link detection. Every page on projectforge.org includes this via the default layout.

Source: GitHub

151 lines · 147 code · 0 comments · 4 blank
CommitMessage
ac75fabf32021-08-10 Jekyll site migration
Data-driven navigation. The navbar doesn't hardcode any links. ALL navigation items come from site.data.navigation_header — a YAML file (_data/navigation_header.yml). The template iterates over .left and .right arrays, rendering links, dropdowns, and buttons dynamically. Changing the nav is a YAML edit, not an HTML edit.

Architecture — 5 horizontal zones

<div data-uk-navbar>                    <!-- UIkit flex container -->
  <div class="uk-navbar-left">           <!-- ZONE 1: Logo + desktop nav links -->
    <a class="uk-logo uk-visible@m">    <!-- Logo (desktop only) -->
    <a class="uk-navbar-toggle uk-hidden@m"> <!-- Hamburger (mobile only) -->
    <ul class="uk-navbar-nav uk-visible@m"> <!-- LEFT nav items from YAML -->
  </div>
  <div class="uk-navbar-center uk-hidden@m"> <!-- ZONE 2: Logo (mobile center) -->
  </div>
  <div class="uk-navbar-right">         <!-- ZONE 3: Search + RIGHT nav items -->
    <!-- SimpleJekyllSearch toggle -->
    <ul class="uk-navbar-nav uk-visible@m"> <!-- RIGHT nav items from YAML -->
    <a class="uk-navbar-toggle uk-hidden@m"> <!-- Mobile site nav toggle -->
  </div>
</div>

The navbar has 5 logical zones arranged via UIkit's flex navbar component. Zones 1 and 3 are the main content areas (desktop), zone 2 is a mobile-only centered logo, and zones 4-5 are implicit (the search dropdown and mobile offcanvas toggle).

The uk-visible@m / uk-hidden@m pattern appears throughout — desktop elements are hidden on mobile, mobile elements are hidden on desktop. The breakpoint @m is 960px (from $breakpoint-medium in variables.scss #4091).

The Liquid templating engine — data-driven navigation

{% for link in site.data.navigation_header.left %}    <!-- Iterate YAML data -->
  {% if link.url contains 'http' %}                    <!-- External link detection -->
    {% assign domain = '' %}                           <!-- No relative_url for http links -->
  {% else %}
    {% assign domain = '' | relative_url %}             <!-- Prepend base URL for internal -->
  {% endif %}
  {% if link.url == page.url %}                        <!-- Active page detection -->
    {% assign current = ' class="uk-active"' %}        <!-- Highlight current page -->
  {% endif %}
  {% if link.button %}                                 <!-- Button-styled nav item -->
    <li><a class="uk-button uk-button-{{ link.button }}"> <!-- Dynamic button style -->
  {% elsif link.url %}
    <li{{ current }}><a href="{{ domain }}{{ link.url }}"> <!-- Standard nav link -->
  {% else %}
    <!-- Dropdown: link without URL → section header -->
    <div class="uk-navbar-dropdown">
      {% for child in link.dropdown %}                 <!-- Nested loop for children -->
        {% if child.url %}
          <li><a href="...">{{ child.title }}</a></li>
        {% else %}
          <li class="uk-nav-header">{{ child.title }}</li> <!-- Section header -->
        {% endif %}
      {% endfor %}
    </div>
  {% endif %}
{% endfor %}

The nav rendering logic has three branches per link item:

BranchConditionRenders
Buttonlink.button is setA button-styled nav item (.uk-button-{{ link.button }}). Button style (primary/secondary/danger) comes from YAML.
Standard linklink.url exists, no buttonA regular <a> link. Gets .uk-active class if link.url == page.url.
DropdownNeither url nor buttonA dropdown menu with nested {% for %} loop. Children with URLs become links; children without become .uk-nav-header section headers.

External link detection: {% if link.url contains 'http' %} — if the URL contains http, domain is set to empty string (the URL is absolute). Otherwise, relative_url filter prepends the site's base URL. This gracefully handles both internal doc links and external links (GitHub, SourceForge).

Active page detection: {% if link.url == page.url %} — exact string comparison between the link's URL and the current page's URL. If they match, the .uk-active class is added, which UIkit styles with the active nav item appearance. This is simple but fragile — it won't match if one URL has a trailing slash and the other doesn't.

Search — SimpleJekyllSearch, NOT Tipue

<script>
SimpleJekyllSearch({
  searchInput: document.getElementById('search-navbar'),
  resultsContainer: document.getElementById('search-navbar-results'),
  noResultsText: '...',
  searchResultTemplate: '<li><a href="{url}">{title}</a></li>',
  json: "{{ '/search.json' | relative_url }}"
});
searchResults("search-navbar");
</script>

Two different search implementations coexist on the site:

SearchLibraryWhereData source
Navbar searchSimpleJekyllSearchNavbar toggle (desktop)search.json (#4094)
Page searchTipue Search/search/ (#4093)search.json (#4094)

Both use the SAME data source (search.json) but different JavaScript libraries. SimpleJekyllSearch is simpler — it takes a JSON file, an input element, and a results container, and does client-side filtering. It's used in the navbar because it's lighter than Tipue and works well for a compact dropdown results list.

The searchResults("search-navbar") call on line 86 is a custom function (not from SimpleJekyllSearch) — likely defined elsewhere to handle the toggle behavior (show/hide results on input focus).

Mobile — dual offcanvas system

The navbar has TWO separate mobile menus:

ToggleTargetVisible text
Left hamburger#offcanvas-docs"Documentation" (from site.data.translation[site.lang].mobile_nav_docs)
Right hamburger#offcanvas"Site" (from site.data.translation[site.lang].mobile_nav_site)

This dual-menu pattern separates documentation navigation from site navigation on mobile — a thoughtful UX decision. The translation keys (mobile_nav_docs, mobile_nav_site) enable multi-language support. If translations are absent (lines 9-10, 143-144), the hamburger appears without text — just the icon.

The offcanvas panels themselves are defined in separate include files (offcanvas.html, offcanvas-docs.html), not in this template.

Sticky behavior — the animation contract

data-uk-sticky="animation: uk-animation-slide-top;
  sel-target: .uk-navbar-container;
  cls-active: uk-navbar-sticky;
  cls-inactive: uk-navbar-transparent;
  top: 200"

UIkit's Sticky component is configured via a data attribute string with semicolon-separated parameters: