<div class="anchor-links-container anchor-links-container-dark">
    <nav class="anchor-links anchor-links-dark anchor-links-stacked" role="navigation" aria-labelledby="anchor-links-title-label" aria-expanded="true" data-gumshoe-header data-scroll-header>
        <div class="anchor-links-title">
            <a href="#" class="anchor-links-title-link" aria-controls="page-content-navigation">
                <span id="anchor-links-title-label" class="anchor-links-title-label">On this page</span>
            </a>
            <a href="#" class="anchor-links-title-toggle" aria-label="Toggle Page Content Navigation">
                <i class="uod-icons uod-icons-menu anchor-links-title-toggle-icon"></i>
            </a>
        </div>
        <div class="anchor-links-list-container">
            <ul id="page-content-navigation" class="anchor-links-list" data-gumshoe>
                <li class="anchor-links-list-item">
                    <a href="#course-description" class="anchor-links-list-item-link" data-scroll>Course description</a>
                </li>
                <li class="anchor-links-list-item">
                    <a href="#what-will-you-study" class="anchor-links-list-item-link" data-scroll>What will you study</a>
                </li>
                <li class="anchor-links-list-item">
                    <a href="#how-will-you-learn" class="anchor-links-list-item-link" data-scroll>How will you learn</a>
                </li>
                <li class="anchor-links-list-item">
                    <a href="#entry-requirements" class="anchor-links-list-item-link" data-scroll>Entry requirements</a>
                </li>
                <li class="anchor-links-list-item">
                    <a href="#fees-and-funding" class="anchor-links-list-item-link" data-scroll>Fees and funding</a>
                </li>
                <li class="anchor-links-list-item">
                    <a href="#how-to-apply" class="anchor-links-list-item-link" data-scroll>How to apply</a>
                </li>
                <li class="anchor-links-list-item">
                    <a href="#careers" class="anchor-links-list-item-link" data-scroll>Careers</a>
                </li>
            </ul>
        </div>
    </nav>
</div>
<div class="anchor-links-container anchor-links-container-{% if light %}light{% else %}dark{% endif %}">
    <nav class="anchor-links anchor-links-{% if light %}light{% else %}dark{% endif %} anchor-links-{% if inline %}inline{% else %}stacked{% endif %}" role="navigation" aria-labelledby="anchor-links-title-label" aria-expanded="true" data-gumshoe-header data-scroll-header>
        <div class="anchor-links-title">
            <a href="#" class="anchor-links-title-link" aria-controls="page-content-navigation">
                <span id="anchor-links-title-label" class="anchor-links-title-label">{% if titleLabel %}{{ titleLabel }}{% else %}On this page{% endif %}</span>
            </a>
            <a href="#" class="anchor-links-title-toggle" aria-label="Toggle Page Content Navigation">
                <i class="uod-icons uod-icons-menu anchor-links-title-toggle-icon"></i>
            </a>
        </div>
        <div class="anchor-links-list-container">
            <ul id="page-content-navigation" class="anchor-links-list" data-gumshoe>
                {% for index, link in links %}
                <li class="anchor-links-list-item">
                    <a href="#{{ link.landmark }}" class="anchor-links-list-item-link{% if link.disabled %} disabled{% endif %}" data-scroll{% if link.disabled %} onclick="return false;" onkeyup="return false;"{% endif %}{% if link.title %} title="{{ link.title }}"{% endif %}>{{ link.label }}</a>
                </li>
                {% endfor %}
            </ul>
        </div>
    </nav>
</div>
{
  "links": [
    {
      "label": "Course description",
      "landmark": "course-description"
    },
    {
      "label": "What will you study",
      "landmark": "what-will-you-study"
    },
    {
      "label": "How will you learn",
      "landmark": "how-will-you-learn"
    },
    {
      "label": "Entry requirements",
      "landmark": "entry-requirements"
    },
    {
      "label": "Fees and funding",
      "landmark": "fees-and-funding"
    },
    {
      "label": "How to apply",
      "landmark": "how-to-apply"
    },
    {
      "label": "Careers",
      "landmark": "careers"
    }
  ]
}
  • Content:
    import gumshoe from 'gumshoe'
    import 'smooth-scroll/dist/js/smooth-scroll.polyfills.js'
    import SmoothScroll from 'smooth-scroll'
    import stickybits from 'stickybits'
    
    // only run the following code if there's an anchor links navigation on the page
    let elementExistsOnPage = $('[data-gumshoe-header]').length > 0;
    if (elementExistsOnPage) {
        // for linking the scroll position and the state of the navigation links
        gumshoe.init({
            offset: 40,
            callback: (nav) => {
                // if no navigation element is in focus, default to selecting the first one.
                if (!nav) {
                    $('[data-gumshoe] li:first-child, [data-gumshoe] li:first-child a').each((el) => {
                        $(el).addClass('active')
                    })
                }
            }
        });
    
        // make the navigation sticky
        stickybits('.anchor-links-container', {
            useStickyClasses: true
        });
    
        // toggle the expanded state of the mobile navigation
        $('.anchor-links-title-link, .anchor-links-title-toggle').on('click', (e) => {
            e.preventDefault(); // because we don't want the page to jump around
            $('.anchor-links').toggleClass('active');
            // $('.anchor-links-title-toggle-icon').toggleClass('uod-icons-menu');
            // $('.anchor-links-title-toggle-icon').toggleClass('uod-icons-cross');
        });
    
        // add functionality to the close button
        // $('.anchor-links-title-close').on('click', (e) => {
        //     e.preventDefault(); // because we don't want the page to jump around
        //     $('.anchor-links').removeClass('active');
        // });
    
        // add functionality to the links in mobile view
        $('.anchor-links-list-item-link').on('click', (e) => {
            $('.anchor-links').removeClass('active');
        });
    
        // for smooth scrolling behaviours when clicking links in the navigation
        new SmoothScroll('a[data-scroll]', {
            header: '[data-scroll-header]',
            offset: 40
        });
    }
    
  • URL: /components/raw/anchor-links/anchor-links.js
  • Filesystem Path: components/components/anchor-links/anchor-links.js
  • Size: 2 KB
  • Content:
    .anchor-links {
        transition: box-shadow .5s $default-animation-curve;
        box-shadow: none;
    
        @include for-largerthan-ipad-portrait {
            flex-direction: row;
            box-shadow: none;
        }
    
        box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.25);
    
        @include for-largerthan-ipad-portrait {
            box-shadow: none;
        }
    
        &-container {
            @include margin-small;
            position: relative;
            z-index: 5;
            height: px-to-em(60, 18);
            max-width: 100%;
    
            @include for-largerthan-ipad-portrait {
                height: px-to-em(78, 18);
                width: 100vw;
                flex-direction: row;
            }
    
            &-dark {
                background: $light-grey;
            
                @include for-largerthan-ipad-portrait {
                    box-shadow: none;
                    transition: box-shadow .5s $default-animation-curve;
                    box-shadow: none;
                    box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.25);
                }
            }
        }
    
        &-title {
            @include body-font;
            display: flex;
            height: px-to-em(60, 18);
    
            @include for-largerthan-ipad-portrait {
                display: none;
            }
    
            a {
                @include disable-underlines;
            }
    
            .anchor-links-dark & {
                @include full-bleed;
            }
    
            &-link {
                @include link-colour($text-black);
                flex: 1;
                padding: 0 fluid-margin();
                line-height: px-to-em(60, 18);
                font-weight: bold;
            }
    
            &-label {
                display: inline-block;
                vertical-align: top;
            }
    
            &-menu-icon {
                margin-right: .5em;
                line-height: px-to-em(60, 25);
                font-size: px-to-em(25, 18);
            }
    
            &-title {
                cursor: initial;
                overflow: hidden;
    
                .anchor-links.active & {
                    cursor: pointer;
    
                    &:hover {
                        .anchor-links-title-close-icon {
                            transform: rotate(90deg);
                        }
                    }
                }
            }
    
            &-toggle-icon {
                transition: transform .3s $default-animation-curve, opacity .3s $default-animation-curve;
                padding: 0 fluid-margin();
                line-height: px-to-em(60, 25);
                color: $text-black;
                font-size: px-to-em(25, 18);
    
                .anchor-links.active & {
                    transform: rotate(90deg);
                    opacity: 1;
    
                    &:before {
                        content: $uod-icons-cross;
                    }
                }
            }
        }
    
        &-list {
            margin: 0 0 1px;
            padding: 0;
            font-size: 0;
            list-style-type: none;
    
            .anchor-links-stacked & {
                display: flex;
                flex-direction: column;
                flex-wrap: nowrap;
    
                @include for-largerthan-ipad-portrait {
                    flex-direction: row;
                }
            }
    
            &-container {
                transition: max-height .5s $default-animation-curve;
                max-height: 0;
                overflow: hidden;
    
                .active & {
                    max-height: 1000px;
                    max-height: calc(100vh - #{px-to-em(60, 18)});
                    overflow-y: auto;
    
                    @include for-up-to-ipad-portrait {
                        padding-bottom: 1px; //account for negative bottom margin on items
                    }
                }
    
                @include for-largerthan-ipad-portrait {
                    max-height: inherit;
                    overflow: visible;
                }
    
                .anchor-links-light & {
    
                    @include for-largerthan-ipad-portrait {
                        border: 2px solid $black;
                        display: inline-block;
                    }
                }
            }
    
            &-item {
                margin: 0;
                height: px-to-em(50, 18);
                min-width: px-to-em(50, 18);
                word-wrap: break-word;
                overflow-wrap: break-word;
    
                &:last-child {
                    border-bottom: 0;
                }
    
                a {
                    @include underline-only-on-hover;
                }
    
                .anchor-links-stacked & {
                    display: flex;
                    flex: 0 1 auto;
                    font-size: 17px;
                    border-bottom: 1px dashed $dark-grey;
    
                    @include for-largerthan-ipad-portrait {
                        height: px-to-em(78, 18);
                        border-bottom: 0;
                        border-left: 1px dashed $dark-grey;
    
                        &:last-child {
                            border-right: 1px dashed $dark-grey;
    
                            &.active {
                                border-right-color: $mid-grey;
                                border-right-style: solid;
                            }
                        }
    
                        &.active,
                        &.active + & {
                            border-left-color: $mid-grey;
                            border-left-style: solid;
                        }
                    }
                }
    
                .anchor-links-inline & {
                    display: inline-block;
                    @include header;
                    font-size: responsive 20px 28px;
                    font-range: $mobile-portrait $tablet-portrait;
    
                    @include for-largerthan-ipad-portrait {
                        min-width: 48px;
                        height: 48px;
                    }
                }
    
                &-link {
                    @include link-colour($text-black);
                    font-family: $header-font;
                    line-height: 1.29;
                    margin-bottom: -1px;
                    transition: background-color .3s $default-animation-curve;
    
                    @include for-largerthan-ipad-portrait {
                        font-size: 17px;
                        margin-bottom: 0;
                    }
    
                    .anchor-links-stacked & {
                        align-items: center;
                        display: flex;
                        flex: 1 1 auto;
                        font-size: 17px;
                        padding: 1em;
                        @include link-colour($text-black);
                    }
    
                    .anchor-links-inline & {
                        font-size: responsive 20px 28px;
                        font-range: $mobile-portrait $tablet-portrait;
                        // padding: .25em .5em;
                        text-align: center;
                        @include link-colour($primary-blue);
                        line-height: px-to-em(50, 18);
                        display: block;
    
                        @include for-largerthan-ipad-portrait {
                            line-height: 48px;
                        }
                    }
    
                    &.active {
                        background: $mid-grey;
                    }
    
                    &.disabled {
                        @include link-colour($mid-grey);
                    }
                }
            }
        }
    
        &-dark {
            @include full-bleed-inset;
            background: $light-grey;
        }
    
        &-light {
            background: $white;
            margin-left: $min-size;
            margin-right: $min-size;
            border: 2px solid $black;
    
            @include for-largerthan-ipad-portrait {
                text-align: center;
                border: none;
            }
        }
    }
    
  • URL: /components/raw/anchor-links/anchor-links.scss
  • Filesystem Path: components/components/anchor-links/anchor-links.scss
  • Size: 7.3 KB

Anchor Links Component

A on-page navigation and indicator, with animated scrolling and sticky behaviour to keep the menu on screen at all times. On desktop devices this is a horizontal menu and on mobile it collapses to a toggling drop-down style menu. Clicking on a link in the menu will transition the page to that point in the content and automatically collapse the menu to make the content visible if on mobile.

Labels for the menu options and the id element that they reference are specified separately in the configuration. Successful navigation requires a landmark element to exist elsewhere on the page which matches the configuration.

Key Features

  • Accessible markup with ARIA metadata
  • Progressively enhanced - functions without Javascript enabled
  • CSS Sticky behaviours with Javascript fallback for browsers which do not support position:sticky
  • Animated scrolling
  • Current location indication
  • Responsive UI

Properties

Text Block

  • links [Required, Array of Landmark]

Landmark

  • label [required, string]
  • landmark [required, string]