The @uol-header-local-navigation presents a set of site specific top level links allowing visitors to navigate the local site.
The purpose of this component is to provide consistent navigation throughout the site allowing users to get to key areas within the site easily.
The local navigation must remain consistent throughout the site, ie. if two pages are on the same domain or subdomain the navigation should always be the same.
Examples:
must have the same navigation
must have the same navigation
The local navigation should always start with a “Home” link that returns the user to the site root.
The local navigation supports a single level of sub-navigation to support groups of related links. Example
We do not recommend having more than one set of sub-navigation within the local navigation, as illustrated.
The sub-navigation items may also include CTAs (Calls to action). These items are styled differently and should be located at the end of the sub-navigation group.
Once users have navigated to a section of the site using the local navigation, additional navigation can be provided using the @uol-section-nav.
If the current page matches a link from the local navigation, the matching navigation link should be set as “current”: true.
Example:
{
  "title": "Home",
  "url": "/",
  "current": true
},The Local navigation: Highlight Active Page variant shows how one item (other than the home page) are given this value. Specifying “current” as true adds aria-current=”page” on to the link (a tag). Setting this parameter also changes the style of this link to signify this is the current page.
When implementing this header, ensure that aria-current=”page” is added to the link representing the current page and only this page.
This also helps Assistive Technology users know that the link will not take them to a different page.
Only navigation items within the sub-navigation may use “cta”: true. Items identified as CTAs should be grouped together at the end of a sub-navigation group. See component example context for illustration.
        {% if items.length %}
<nav class="uol-header-local-navigation-wrapper uol-content-container" aria-label="Site navigation">
  <ul class="uol-header-local-navigation">
    {% for item in items %}
      <li class="uol-header-local-navigation__item {{ 'uol-header-local-navigation__item--parent' if item.items.length }}" data-label="{{ item.title }}">
        {% if item.url %}
          <a class="uol-header-local-navigation__link" href="{{ item.url }}" {% if item.current %}aria-current="page"{% endif %}>
        {% endif %}
        {{ item.title }}
        {% if item.url %}
          </a>
        {% endif %}
        {% if item.items.length %}
          <ul class="uol-header-local-navigation__sub-nav" data-title="{{ item.title }}">
            {% for item in item.items %}
              <li class="uol-header-local-navigation__item {{ 'uol-header-local-navigation__item--cta' if item.cta }}">
                {% if item.url %}
                  <a class="uol-header-local-navigation__link uol-header-local-navigation__link--sub-nav" href="{{ item.url }}"  {% if item.current %}aria-current="page"{% endif %}>
                {% endif %}
                {{ item.title | safe }}
                {% if item.url %}
                  </a>
                {% endif %}
              </li>
            {% endfor %}
          </ul>
        {% endif %}
      </li>
    {% endfor %}
  </ul>
</nav>
{% endif %}
    
        
        <nav class="uol-header-local-navigation-wrapper uol-content-container" aria-label="Site navigation">
    <ul class="uol-header-local-navigation">
        <li class="uol-header-local-navigation__item " data-label="Home">
            <a class="uol-header-local-navigation__link" href="/">
                Home
            </a>
        </li>
        <li class="uol-header-local-navigation__item " data-label="Active page">
            <a class="uol-header-local-navigation__link" href="/" aria-current="page">
                Active page
            </a>
        </li>
        <li class="uol-header-local-navigation__item " data-label="Another page">
            <a class="uol-header-local-navigation__link" href="/">
                Another page
            </a>
        </li>
    </ul>
</nav>
        
    
                                // Utility
.no-wrap {
  white-space: nowrap;
}
@mixin local-navigation-link-accent {
  &::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: calc(#{$spacing-4} - 2px);
    right: calc(100% - #{$spacing-4} + 2px);
    height: $spacing-2;
    background-color: $color-white;
    transition: right 0.4s ease 0.1s;
  }
  &:focus {
    outline: 2px dotted transparent;
  }
  &:hover,
  &:focus {
    &::after {
      right: calc(#{$spacing-4} - 2px);
      background-color: $color-brand;
      @media (-ms-high-contrast: active),
      (forced-colors: active) {
        background-color: WindowText;
        background-color: CanvasText;
      }
    }
  }
}
.uol-header-local-navigation-wrapper {
  max-width: 101.75rem;
  .no-js & {
    margin: $spacing-4 0;
    padding-bottom: $spacing-4;
  }
  .js & {
    position: relative;
    padding-top: $spacing-2;
    padding-bottom: $spacing-2;
    margin-top: 0;
    @include media(">=uol-media-l") {
      margin-top: $spacing-6;
      padding-left: $spacing-2;
    }
    @include media(">=uol-media-xl") {
      padding-left: $spacing-4;
    }
    @include media(">=uol-media-xxl") {
      padding-left: 0;
    }
  }
}
  .uol-header-local-navigation-wrapper--scrollable {
    border-top: 1px solid $color-border--light;
  }
  .uol-header-local-navigation-wrapper--sub-expanded {
    @include media("<uol-media-m") {
      padding-bottom: 0;
    }
  }
.uol-header-local-navigation {
  .js & {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -ms-overflow-style: none;
    scrollbar-width: none;
    &::-webkit-scrollbar {
        display: none;
    }
  }
}
  .uol-header-local-navigation--scrollable {
    .js & {
      margin-left: $spacing-6;
      margin-right: $spacing-6;
    }
    .uol-header-local-navigation-wrapper--sub-expanded & {
      @include media("<uol-media-m") {
        margin-left: 0;
        margin-right: 0;
      }
    }
  }
  .uol-header-local-navigation__button-scroll {
    position: absolute;
    top: 0;
    z-index: 1;
    width: $spacing-7;
    height: 100%;
    background: rgba($color-white, 0.96);
    border: none;
    transition: opacity 0.3s ease;
    &::after {
      content: "";
      display: block;
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 0;
      height: 0;
      border: 7px solid transparent;
      @media (-ms-high-contrast: active), (forced-colors: active) {
        -ms-high-contrast-adjust: none;
        forced-color-adjust: CanvasText;
      }
    }
    &[disabled] {
      // background: none;
      opacity: 0;
      cursor: default;
      z-index: -1;
    }
    .uol-header-local-navigation-wrapper--sub-expanded & {
      @include media("<uol-media-m") {
        display: none;
      }
    }
  }
    .uol-header-local-navigation__button-scroll--left {
      left: 0;
      border-right: 1px solid $color-border--light;
      &::after {
        left: $spacing-3;
        border-right-color: $color-brand--bright;
      }
    }
    .uol-header-local-navigation__button-scroll--right {
      right: 0;
      border-left: 1px solid $color-border--light;
      &::after {
        right: $spacing-3;
        border-left-color: $color-brand--bright;
      }
    }
  .uol-header-local-navigation__item {
    font-weight: $font-weight-bold--sans-serif;
    @include media("<uol-media-m") {
      font-weight: 500; // TODO: May need to update font weights
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
    }
    .js .uol-header-local-navigation__sub-nav & {
      padding: $spacing-2 $spacing-4;
    }
  }
    .uol-header-local-navigation__item--sibling-open {
      @include media("<uol-media-m") {
        position: absolute;
        clip: rect(1px, 1px, 1px, 1px);
        height: 1px;
        width: 1px;
        overflow: hidden;
        left: -9999px;
      }
    }
    .uol-header-local-navigation__item--cta {
      .js & {
        background: $color-black;
        margin-left: -1px;
        margin-right: -1px;
        margin-bottom: -1px;
        border: 1px solid $color-black;
        @include media(">=uol-media-m") {
          &:last-of-type {
            border-radius: 0 0 $spacing-3 $spacing-3;
          }
        }
      }
      .js .uol-header-local-navigation__sub-nav & {
        // Add extra spacing to first and last cta (assuming cta is last item)
        margin-top: $spacing-4;
        padding-top: $spacing-5;
        padding-bottom: $spacing-2;
        ~ .uol-header-local-navigation__item--cta {
          margin-top: 0;
          padding-top: 0;
        }
        &:last-of-type {
          padding-bottom: $spacing-6;
        }
      }
    }
  .uol-header-local-navigation__link {
    .no-js & {
      // padding: $spacing-1 $spacing-2 $spacing-2;
      &:hover,
      &:focus {
        text-decoration: underline;
      }
    }
    .js & {
      @extend %text-size-paragraph;
      @include local-navigation-link-accent();
      position: relative;
      display: block;
      color: $color-font--light;
      text-decoration: none;
      white-space: nowrap;
      padding: $spacing-2 $spacing-4 $spacing-3;
      transition: color 0.4s ease 0.1s;
      &:hover,
      &:focus {
        color: $color-font--dark;
      }
      &[aria-current] {
        color: $color-brand;
      }
    }
    .js .uol-header-local-navigation__sub-nav & {
      background: $color-white;
      color: $color-font--light;
      white-space: normal;
      transition: background 0.5s ease;
      border-radius: 6px;
      -webkit-box-sizing: border-box;
      box-sizing: border-box;
      &:hover,
      &:focus {
        background-color: $colour-warmgrey—faded;
        color: $color-font;
        text-decoration: underline;
      }
      &[aria-current] {
        color: $color-brand;
      }
      &::after {
        content: none;
      }
      svg {
        position: relative;
        top: 0.35em;
        left: 0;
        margin-left: 0.5em;
        fill: rgba($color-brand, 0);
        transition: all 0.3s ease 0.2s;
      }
      &:hover,
      &:focus {
        svg {
          left: 0.4em;
          fill: $color-brand;
          @media (-ms-high-contrast: active) {
            fill: windowText;
          }
        }
      }
    }
    .js .uol-header-local-navigation__item--cta & {
      background: inherit;
      color: $color-white;
      padding-top: $spacing-1;
      padding-bottom: $spacing-1;
      @include media(">=uol-media-m") {
        font-size: 18px;
      }
      &:hover,
      &:focus {
        background-color: $color-black--dark;
        color: $color-white;
      }
      &[aria-current] {
        color: $color-brand--bright;
      }
      svg {
        fill: $color-brand--bright;
        @media (-ms-high-contrast: active) {
          fill: windowText;
        }
      }
      &:hover,
      &:focus {
        svg {
          left: 0.4em;
          fill: $color-brand--bright;
          @media (-ms-high-contrast: active) {
            fill: windowText;
          }
        }
      }
    }
  }
  .uol-header-local-navigation__subnav-button {
    @extend %text-size-paragraph;
    @include local-navigation-link-accent();
    position: relative;
    font-weight: inherit;
    white-space: nowrap;
    background: transparent;
    border: none;
    margin: 0;
    padding: $spacing-2 $spacing-4 $spacing-3;
    appearance: none;
    color: $color-font--light;
    transition: color 0.4s ease 0.1s;
    &[aria-expanded="true"] {
      @include media("<=uol-media-m") {
        &:focus {
          text-decoration: underline;
        }
        &:focus:not(:focus-visible) {
          text-decoration: none;
        }
        &:focus-visible {
          outline-color: transparent;
          text-decoration: underline;
          text-underline-offset: 4px;
          text-decoration-thickness: $spacing-2;
          text-decoration-color: $color-brand--bright;
        }
      }
    }
    &:focus {
      outline-offset: -4px;
    }
    &:hover,
    &:focus {
      color: $color-font--dark;
    }
    &[aria-expanded="true"] {
      color: $color-font--dark;
      @include media(">=uol-media-m") {
        &::before {
          content: "";
          position: absolute;
          right: 0;
          transform: translateX(-70%);
          bottom: ($spacing-2 - $spacing-1 / 2);
          width: 0;
          height: 0;
          border: ($spacing-3 - $spacing-1 / 2) solid transparent;
          border-top: none;
          border-bottom-color: $color-white;
          filter: drop-shadow(0 -0.05rem 0.05rem rgba($color-black, 0.1));
          z-index: 4;
        }
      }
    }
    // TODO: Un-nest icon
    .uol-header-local-navigation__button__icon {
      position: relative;
      display: inline-block;
      width: $spacing-5;
      height: $spacing-4;
      margin-left: 0.2em;
      margin-right: -0.2em;
      &::before,
      &::after {
        content: "";
        display: block;
        position: absolute;
        width: 0.5em;
        height: 0.13em;
        background: $color-black;
        bottom: 20%;
        @media (-ms-high-contrast: active) {
          background: ButtonText;
        }
      }
      &::before {
        left: 50%;
        transform: translateX(-76%) rotate(45deg);
      }
      &::after {
        right: 50%;
        transform: translateX(76%) rotate(-45deg);
      }
    }
    // TODO: Duplicate selector - refactor
    &[aria-expanded="true"] {
      @include media("<uol-media-m") {
        @include font-size-responsive(1.75rem, 2.25rem, 3rem);
        @include line-height-responsive(2.25rem, 3rem, 3.5rem);
        display: block;
        text-align: left;
        width: 100vw;
        border-bottom: 2px solid $color-border--light;
        @include media("<uol-media-m") {
          margin-top: -#{$spacing-2};
          padding: 1.312rem $spacing-7 1.312rem $spacing-4;
        }
        &::after {
          content: none;
        }
        @include media(">=uol-media-m") {
          &::before {
            content: "";
            width: 30px;
            height: 30px;
            background: red;
            position: absolute;
          }
        }
      }
      // TODO: Un-nest icon
      .uol-header-local-navigation__button__icon {
        &::before {
          transform: translateX(-76%) rotate(315deg);
        }
        &::after {
          transform: translateX(76%) rotate(45deg);
        }
        @include media("<uol-media-m") {
          position: absolute;
          padding: $spacing-4;
          right: $spacing-3;
          top: 50%;
          transform: translateY(-50%);
          &::before {
            bottom: 50%;
          }
          &::after {
            bottom: 50%;
          }
        }
      }
    }
  }
  .uol-header-local-navigation__sub-nav {
    .js & {
      padding: 0;
      list-style: none;
      display: none;
      box-shadow:
        0 15px 24px 0 rgba(10, 2, 2, 0.15),
        0 5px 10px 0 rgba(33, 33, 33, 0.5),
        0 5px 15px 2px rgba(255, 255, 255, 0.25);
      @include media("<uol-media-m") {
        padding-top: $spacing-4;
        background: $color-white;
        width: 100vw;
        overflow-y: auto;
        overflow: hidden;
        @media (-ms-high-contrast: active) {
          border-bottom: 1px solid windowText;
        }
      }
      @include media(">=uol-media-m") {
        position: absolute;
        top: calc(100% - #{$spacing-4});
        box-sizing: border-box;
        width: 375px;
        background: $color-white;
        border: 1px solid rgba($color-border--light, 0.2);
        border-radius: $spacing-3;
        padding-top: $spacing-1;
        z-index: 1001;
        transform: translateX(-#{$spacing-4});
      }
    }
  }
                            
                            
                        
                                const localNavigations = document.querySelectorAll('.uol-header-local-navigation')
const svgRightArrow = `<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" focusable="false" aria-hidden="true">
  <path d="M0 0h24v24H0z" fill="none"/>
  <path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"/>
</svg>`
// TODO: Move to utilities
const getSiblings = (elem) => {
  return Array.prototype.filter.call(elem.parentNode.children, (sibling) => {
    return sibling !== elem
  })
}
const closestByClass = (elem, classSelector) => {
  // Traverse the DOM up with a while loop
  while (elem.className != classSelector) {
    // Increment the loop to the parent node
    elem = elem.parentNode
    if (!elem) {
      return null
    }
  }
  return elem
}
export const localNavigationDropdown = () => {
  const subNavOpen = (navigation, navBtn, subNav, siblings) => {
    navBtn.setAttribute('aria-expanded', 'true')
    subNav.style.display = 'block'
    // Toggle the siblings
    siblings.forEach((sibling) => {
      sibling.classList.add('uol-header-local-navigation__item--sibling-open')
    })
    navigation.classList.add('uol-header-local-navigation--sub-expanded')
    navigation.parentNode.classList.add('uol-header-local-navigation-wrapper--sub-expanded')
  }
  const subNavClose = (navigation, navBtn, subNav, siblings) => {
    subNav.style.display = 'none'
    navBtn.setAttribute('aria-expanded', 'false')
    // Toggle the siblings
    siblings.forEach((sibling) => {
      sibling.classList.remove('uol-header-local-navigation__item--sibling-open')
    })
    navigation.classList.remove('uol-header-local-navigation--sub-expanded')
    navigation.parentNode.classList.remove('uol-header-local-navigation-wrapper--sub-expanded')
    navigation.dispatchEvent(new CustomEvent('scroll'))
  }
  localNavigations.forEach( (navigation) => {
    const navItems = navigation.querySelectorAll('.uol-header-local-navigation__item')
    navItems.forEach( (navItem) => {
      // Scroll items fully into view (exclude sub nav)
      if (!navItem.parentNode.classList.contains('uol-header-local-navigation__sub-nav')) {
        const navLink = navItem.querySelector('.uol-header-local-navigation__link')
        navLink.addEventListener('focus', () => {
          navItem.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'start'
          })
        })
      }
      const subNav = navItem.querySelector('.uol-header-local-navigation__sub-nav')
      if (subNav) {
        const siblings = getSiblings(navItem)
        const navLabel = navItem.dataset.label
        const subNavItems = subNav.querySelectorAll('.uol-header-local-navigation__item')
        // Hide subNav
        subNav.style.display = 'none'
        // Remove text nodes
        navItem.childNodes.forEach( childNode => {
          if (childNode.nodeName === '#text') {
            childNode.remove()
          }
        })
        // TODO Add support for top level links
        // Add button
        let navBtn = document.createElement('button')
        navBtn.type = 'button'
        navBtn.classList.add('uol-header-local-navigation__subnav-button')
        navBtn.innerHTML = navLabel + '<span class="uol-header-local-navigation__button__icon"></span>'
        navBtn.setAttribute('aria-expanded', false)
        navItem.prepend(navBtn)
        navBtn.onclick = () => {
          let expanded = navBtn.getAttribute('aria-expanded') === 'true' || false
          if (!expanded) {
            subNavOpen(navigation, navBtn, subNav, siblings)
          } else {
            subNavClose(navigation, navBtn, subNav, siblings)
          }
        }
        // Listen for focusout and close subnav when focus leaves the item's descendants
        navItem.addEventListener('focusout', (event) => {
          if (event.relatedTarget) {
            // If the new focused element is not a child of a sub nav
            if (!closestByClass(event.relatedTarget, 'uol-header-local-navigation__sub-nav')) {
              // if not this navigation item's toggle button
              if(event.relatedTarget.parentNode != navItem) {
                subNavClose(navigation, navBtn, subNav, siblings)
              }
            }
          }
        })
        // Close on click outside
        document.addEventListener("click", (evt) => {
          let targetElement = evt.target; // clicked element
          do {
              if (targetElement == navItem) {
                  // This is a click inside. Do nothing, just return.
                  return;
              }
              // Go up the DOM
              targetElement = targetElement.parentNode;
          } while (targetElement)
          // This is a click outside.
          subNavClose(navigation, navBtn, subNav, siblings)
        })
        // Close on escape
        window.addEventListener("keydown", function(event) {
          if (event.key === 'Escape' || event.keyCode === 27) {
            subNavClose(navigation, navBtn, subNav, siblings)
            // If focus is on a subnav return focus to parent toggle button
            if (event.target.classList.contains('uol-header-local-navigation__link--sub-nav')) {
              const parentButton = event.target.closest('.uol-header-local-navigation__item--parent').querySelector('.uol-header-local-navigation__subnav-button')
              if (parentButton) {
                parentButton.focus()
              }
            }
          }
        }, true);
        // Add arrows to subnav links
        subNavItems.forEach( (item) => {
          const itemLink = item.querySelector('.uol-header-local-navigation__link')
          // Split text to array
          const innerTextArray = itemLink.innerText.trim().split(' ')
          // Wrap last word and svg in no-wrap span to avoid wrapping
          itemLink.innerHTML = innerTextArray.slice(0, -1).join(' ') + ' <span class="no-wrap">' + innerTextArray[innerTextArray.length - 1] + '' + svgRightArrow + '</span>'
        })
      }
    })
  })
}
export const localNavigationScrollButtons = () => {
  const createScrollableNav = (navigation) => {
    const viewportWidthScroll = window.innerWidth * 0.5
    if (!navigation.classList.contains('uol-header-local-navigation--scrollable')) {
      const navParent = navigation.parentNode
      // Add class to navigation for JS dependant spacing
      navigation.classList.add('uol-header-local-navigation--scrollable')
      navigation.parentNode.classList.add('uol-header-local-navigation-wrapper--scrollable')
      // Create left scroll button
      const btnLeft = document.createElement('button')
      btnLeft.classList.add('uol-header-local-navigation__button-scroll')
      btnLeft.classList.add('uol-header-local-navigation__button-scroll--left')
      btnLeft.disabled = true
      btnLeft.innerHTML = '<span class="hide-accessible">Scroll navigation left</span>'
      btnLeft.onclick = () => {
        navigation.scrollBy({
          left: -viewportWidthScroll,
          behavior: 'smooth'
        })
      }
      // Create Right scroll button
      const btnRight = document.createElement('button')
      btnRight.classList.add('uol-header-local-navigation__button-scroll')
      btnRight.classList.add('uol-header-local-navigation__button-scroll--right')
      btnRight.innerHTML = '<span class="hide-accessible">Scroll navigation right</span>'
      btnRight.onclick = () => {
        navigation.scrollBy({
          left: viewportWidthScroll,
          behavior: 'smooth'
        })
      }
      // Prepend/Append scroll buttons
      navParent.prepend(btnLeft)
      navParent.append(btnRight)
      // listen for scrolling end and update scroll buttons disabled state
      let navIsScrolling
      navigation.addEventListener('scroll', () => {
        window.clearTimeout( navIsScrolling )
        navIsScrolling = setTimeout(() => {
          // The following have a 5px tolerance to support browsers that do not support scroll snapping
          // If very near the beginning of scroll
          if (navigation.scrollLeft <= 5) {
            btnLeft.disabled = true
          } else {
            btnLeft.disabled = false
          }
          // If very near the end of scroll
          if ((navigation.scrollLeft + navigation.clientWidth) - navigation.scrollWidth >= -5 ) {
            btnRight.disabled = true
          } else {
            btnRight.disabled = false
          }
        }, 66)
      }, false)
    }
  }
  const destroyScrollableNav = (navigation) => {
    // Remove class to navigation for JS dependant spacing
    navigation.classList.remove('uol-header-local-navigation--scrollable')
    navigation.parentNode.classList.remove('uol-header-local-navigation-wrapper--scrollable')
    document.querySelectorAll('.uol-header-local-navigation__button-scroll').forEach( (button) => {
      button.remove()
    })
  }
  const initiateScrollableNav = () => {
    localNavigations.forEach((navigation) => {
      if (navigation.scrollWidth - 10 > navigation.clientWidth) { // TODO fix ipad landscape showing scroller
        createScrollableNav(navigation)
      } else {
        destroyScrollableNav(navigation)
      }
    })
  }
  // Initialise
  document.addEventListener('DOMContentLoaded', function() {
    initiateScrollableNav()
  });
  // Fire again on resize
  window.addEventListener('resize', initiateScrollableNav)
  // TODO: Hack fix of nav that is just too wide while webfonts load. Need a better solution
  setTimeout(() => {
    if (typeof(Event) === 'function') {
      // modern browsers
      window.dispatchEvent(new Event('resize'));
    } else {
        // for IE and other old browsers
        // causes deprecation warning on modern browsers
        var evt = window.document.createEvent('UIEvents');
        evt.initUIEvent('resize', true, false, window, 0);
        window.dispatchEvent(evt);
    }
  }, 300)
}
                            
                            
                        
        
            
            {
  "items": [
    {
      "title": "Home",
      "url": "/"
    },
    {
      "title": "Active page",
      "url": "/",
      "current": true
    },
    {
      "title": "Another page",
      "url": "/"
    }
  ]
}