<nav id="example-breadcrumb" aria-label="Breadcrumb" class="cwf-breadcrumb">
    <ol class="cwf-breadcrumb__list">
        <li class="cwf-breadcrumb__item cwf-breadcrumb__item--has-dropdown">
            <button class="cwf-breadcrumb__toggle">
                <i class="fas fa-ellipsis-h cwf-breadcrumb__icon">

                </i>
                <span class="cwf-breadcrumb__text">More</span>
            </button>
            <ol class="cwf-breadcrumb__list cwf-breadcrumb__list--dropdown">
                <li class="cwf-breadcrumb__item">
                    <a href="/" class="cwf-breadcrumb__link">
                        <i class="fas fa-home cwf-breadcrumb__icon"></i>
                        <span class="cwf-breadcrumb__text">Home</span>
                    </a>
                </li>

                <li class="cwf-breadcrumb__item">
                    <a href="#" class="cwf-breadcrumb__link">
                        Parent
                    </a>
                </li>
            </ol>
        </li>
        <li class="cwf-breadcrumb__item">
            <a href="#" class="cwf-breadcrumb__link">
                Child 1
            </a>
        </li>
        <li class="cwf-breadcrumb__item">
            Grandchild 2
        </li>
    </ol>
</nav>
{% set break = false %}

{% set homeItem %}
    <li class="cwf-breadcrumb__item">
        <a href="/" class="cwf-breadcrumb__link">
            <i class="fas fa-home cwf-breadcrumb__icon"></i>
            <span class="cwf-breadcrumb__text">Home</span>
        </a>
    </li>
{% endset %}

<nav id="{{ id ?? 'cwf-breadcrumb' }}"
    aria-label="Breadcrumb"
    class="cwf-breadcrumb">
    <ol class="cwf-breadcrumb__list">
        {% for route in routes if not break %}
            {% if route.name.full == title or route.name == title %}
                {% if route.parents is defined %}
                    {% set parents = route.parents %}
                    {% if (parents|length) > 1 %}
                        <li class="cwf-breadcrumb__item cwf-breadcrumb__item--has-dropdown">
                            <button class="cwf-breadcrumb__toggle">
                                <i class="fas fa-ellipsis-h cwf-breadcrumb__icon">

                                </i>
                                <span class="cwf-breadcrumb__text">More</span>
                            </button>
                            <ol class="cwf-breadcrumb__list cwf-breadcrumb__list--dropdown">
                                {{ homeItem }}
                                {% for parent in parents %}
                                    {% if parent != (parents|last) %}
                                        <li class="cwf-breadcrumb__item">
                                            <a href="{{ parent.href }}"
                                                class="cwf-breadcrumb__link">
                                                {{ parent.name.short
                                                    ?? parent.name.full
                                                    ?? parent.name }}
                                            </a>
                                        </li>
                                    {% endif %}
                                {% endfor %}
                            </ol>
                        </li>
                    {% else %}
                        {{ homeItem }}
                    {% endif %}
                    {% set parent = parents|last %}
                    <li class="cwf-breadcrumb__item">
                        <a href="{{ parent.href }}"
                            class="cwf-breadcrumb__link">
                            {{ parent.name.full ?? parent.name }}
                        </a>
                    </li>
                {% else %}
                    {{ homeItem }}
                {% endif %}
                <li class="cwf-breadcrumb__item">
                    {{ route.name.full ?? route.name }}
                </li>
                {% set break = true %}
            {% endif %}
        {% endfor %}
    </ol>
</nav>

{# CMS implementation note: "The link to the current page has aria-current set to page. If the element representing the current page is not a link, aria-current is optional." Since we can't set the aria-current automatically in T4 we can just not make the last item a link. #}
{
  "id": "example-breadcrumb",
  "routes": [
    {
      "level": 1,
      "name": "Parent",
      "href": "#",
      "children": [
        {
          "name": "Child 1",
          "href": "#",
          "children": [
            {
              "name": "Grandchild 1",
              "href": "#"
            },
            {
              "name": "Grandchild 2",
              "href": "#"
            },
            {
              "name": "Grandchild 3",
              "href": "#"
            }
          ]
        }
      ]
    },
    {
      "level": 2,
      "name": "Child 1",
      "href": "#",
      "parents": [
        {
          "name": "Parent",
          "href": "#",
          "children": [
            {
              "name": "Child 1",
              "href": "#",
              "children": [
                {
                  "name": "Grandchild 1",
                  "href": "#"
                },
                {
                  "name": "Grandchild 2",
                  "href": "#"
                },
                {
                  "name": "Grandchild 3",
                  "href": "#"
                }
              ]
            }
          ]
        }
      ],
      "children": [
        {
          "name": "Grandchild 1",
          "href": "#"
        },
        {
          "name": "Grandchild 2",
          "href": "#"
        },
        {
          "name": "Grandchild 3",
          "href": "#"
        }
      ]
    },
    {
      "level": 3,
      "name": "Grandchild 1",
      "href": "#",
      "parents": [
        {
          "name": "Parent",
          "href": "#",
          "children": [
            {
              "name": "Child 1",
              "href": "#",
              "children": [
                {
                  "name": "Grandchild 1",
                  "href": "#"
                },
                {
                  "name": "Grandchild 2",
                  "href": "#"
                },
                {
                  "name": "Grandchild 3",
                  "href": "#"
                }
              ]
            }
          ]
        },
        {
          "name": "Child 1",
          "href": "#",
          "children": [
            {
              "name": "Grandchild 1",
              "href": "#"
            },
            {
              "name": "Grandchild 2",
              "href": "#"
            },
            {
              "name": "Grandchild 3",
              "href": "#"
            }
          ]
        }
      ]
    },
    {
      "level": 3,
      "name": "Grandchild 2",
      "href": "#",
      "parents": [
        {
          "name": "Parent",
          "href": "#",
          "children": [
            {
              "name": "Child 1",
              "href": "#",
              "children": [
                {
                  "name": "Grandchild 1",
                  "href": "#"
                },
                {
                  "name": "Grandchild 2",
                  "href": "#"
                },
                {
                  "name": "Grandchild 3",
                  "href": "#"
                }
              ]
            }
          ]
        },
        {
          "name": "Child 1",
          "href": "#",
          "children": [
            {
              "name": "Grandchild 1",
              "href": "#"
            },
            {
              "name": "Grandchild 2",
              "href": "#"
            },
            {
              "name": "Grandchild 3",
              "href": "#"
            }
          ]
        }
      ]
    },
    {
      "level": 3,
      "name": "Grandchild 3",
      "href": "#",
      "parents": [
        {
          "name": "Parent",
          "href": "#",
          "children": [
            {
              "name": "Child 1",
              "href": "#",
              "children": [
                {
                  "name": "Grandchild 1",
                  "href": "#"
                },
                {
                  "name": "Grandchild 2",
                  "href": "#"
                },
                {
                  "name": "Grandchild 3",
                  "href": "#"
                }
              ]
            }
          ]
        },
        {
          "name": "Child 1",
          "href": "#",
          "children": [
            {
              "name": "Grandchild 1",
              "href": "#"
            },
            {
              "name": "Grandchild 2",
              "href": "#"
            },
            {
              "name": "Grandchild 3",
              "href": "#"
            }
          ]
        }
      ]
    }
  ],
  "title": "Grandchild 2"
}
  • Content:
    // Breadcrumb component styles
    
    @use "../../shared/animation";
    @use "../../shared/media";
    @use "../../shared/style";
    @use "../../shared/theme";
    
    @use "../../utilities/screen-reader/shared" as screen-reader;
    
    // Selector prefix
    $prefix: "cwf" !default;
    
    // Breadcrumb item separator colors
    $item--separator__border-color: style.color("gray-lightest") !default;
    
    // Breadcrumb link colors
    $link__color--mobile: style.color("gray-light") !default;
    $link__color--desktop: style.color("blue", "accent") !default;
    
    // Breadcrumb dropdown list colors
    $list--dropdown__border-color: style.lighten("white-darkest", 33%) !default;
    $list--dropdown__background-color: style.color("white") !default;
    
    .#{$prefix}-breadcrumb {
        @include style.spacing;
        width: 100%;
        font-family: theme.font--sans-serif();
        --cwf-breadcrumb--separator-color: #{$item--separator__border-color};
        --cwf-breadcrumb--mobile-link-color: #{$link__color--mobile};
        --cwf-breadcrumb--desktop-link-color: #{$link__color--desktop};
        --cwf-breadcrumb__list--dropdown--border-color: #{$list--dropdown__border-color};
        --cwf-breadcrumb__list--dropdown--background-color: #{$list--dropdown__background-color};
    }
    
    .#{$prefix}-breadcrumb__list {
        display: flex;
        justify-content: space-between;
        width: 100%;
        margin: 0;
        padding-bottom: 0;
        padding-left: 0;
        padding-right: 0;
        padding-top: 0;
    
        @include media.breakpoint {
            justify-content: flex-start;
        }
    }
    
    .#{$prefix}-breadcrumb__item {
        display: none;
        align-items: center;
        list-style-type: none;
        padding: 0.5rem 0;
    
        &:first-child {
            display: inline-flex;
            order: 1;
    
            @include media.breakpoint {
                order: 0;
            }
        }
    
        &:nth-last-child(2) {
            display: inline-flex;
    
            & .#{$prefix}-breadcrumb__link {
                display: inline-flex;
                align-items: baseline;
            }
    
            & .#{$prefix}-breadcrumb__link:before {
                display: inline-flex;
                width: 1rem;
                height: 1rem;
                -webkit-font-smoothing: antialiased;
                @include style.icon("\f053"); // Left chevron
    
                @include media.breakpoint {
                    display: none;
                }
            }
    
            & .#{$prefix}-breadcrumb__icon {
                display: none;
            }
    
            & .#{$prefix}-breadcrumb__text {
                @include screen-reader.visible;
            }
        }
    
        @include media.breakpoint {
            display: inline-flex;
    
            &:after {
                content: "";
                display: inline-block;
                height: 0.8rem;
                margin-left: 0.5rem;
                margin-right: 0.5rem;
                border-right: 0.1em solid var(--cwf-breadcrumb--separator-color);
                transform: rotate(20deg);
            }
    
            &:last-child:after {
                display: none;
            }
        }
    }
    
    .#{$prefix}-breadcrumb__link {
        color: var(--cwf-breadcrumb--mobile-link-color);
        text-decoration: none;
    
        @include media.breakpoint {
            color: var(--cwf-breadcrumb--desktop-link-color);
            text-decoration: underline;
        }
    }
    
    .#{$prefix}-breadcrumb__text {
        @include screen-reader.only;
    
        @include media.breakpoint {
            @include screen-reader.visible;
        }
    }
    
    .#{$prefix}-breadcrumb__icon {
        display: inline-block;
    
        @include media.breakpoint {
            display: none;
        }
    }
    
    .#{$prefix}-breadcrumb__item--has-dropdown {
        position: relative;
    
        .#{$prefix}-breadcrumb__list--dropdown {
            display: none;
            position: absolute;
            top: 100%;
            right: 0;
            width: auto;
            flex-direction: column;
            border: 1px solid var(--cwf-breadcrumb__list--dropdown--border-color);
            background-color: var(
                --cwf-breadcrumb__list--dropdown--background-color
            );
    
            @include media.breakpoint {
                right: initial;
                left: 0;
            }
        }
    
        .#{$prefix}-breadcrumb__item {
            display: block;
            padding: 0;
            border-bottom: 1px solid
                var(--cwf-breadcrumb__list--dropdown--border-color);
    
            &:first-child {
                order: 0;
            }
    
            &:first-child,
            &:nth-last-child(2) {
                .#{$prefix}-breadcrumb__icon {
                    display: none;
                }
    
                .#{$prefix}-breadcrumb__text {
                    display: static;
                }
    
                .#{$prefix}-breadcrumb__link:before {
                    display: none;
                }
            }
    
            &:last-child {
                border-bottom: none;
            }
    
            &:after {
                display: none;
            }
        }
    
        .#{$prefix}-breadcrumb__link {
            display: flex !important;
            padding: 0.5rem 1rem;
            text-decoration: none;
            @include animation.transition(background-color, color);
    
            &:hover,
            &:focus {
                background-color: var(--cwf-breadcrumb--desktop-link-color);
                color: var(--cwf-breadcrumb__list--dropdown--background-color);
            }
    
            .#{$prefix}-breadcrumb__text {
                @include screen-reader.visible;
            }
        }
    }
    
    .#{$prefix}-breadcrumb__toggle {
        padding: 0;
        border: none;
        background-color: transparent;
        font-size: 1rem;
        font-weight: bold;
        color: var(--cwf-breadcrumb--mobile-link-color);
    
        @include media.breakpoint {
            color: var(--cwf-breadcrumb--desktop-link-color);
            text-decoration: underline;
        }
    }
    
    .#{$prefix}-breadcrumb__item--expand-dropdown {
        .#{$prefix}-breadcrumb__list--dropdown {
            display: flex;
            @include style.z-index("menu");
        }
    }
    
  • URL: /components/raw/breadcrumb/_index.scss
  • Filesystem Path: components/breadcrumb/_index.scss
  • Size: 5.6 KB
  • Content:
    // The default component class
    import { Component } from '../../shared/component.js';
    
    // Provide functionality to the breadcrumb
    export class Breadcrumb extends Component {
        constructor({
            prefix = 'cwf',
            breadcrumb = 'breadcrumb',
            list = 'breadcrumb__list',
            listIsDropdown = 'breadcrumb__list--dropdown',
            item = 'breadcrumb__item',
            itemHasDropdown = 'breadcrumb__item--has-dropdown',
            itemExpandDropdown = 'breadcrumb__item--expand-dropdown',
            link = 'breadcrumb__link',
            toggle = 'breadcrumb__toggle'
        } = {}) {
            super({
                prefix,
                classes: {
                    breadcrumb,
                    list,
                    listIsDropdown,
                    item,
                    itemHasDropdown,
                    itemExpandDropdown,
                    link,
                    toggle
                }
            });
    
            // Bind "this" to the appropriate methods
            this.breadcrumbKeyboardNavigation =
                this.breadcrumbKeyboardNavigation.bind(this);
            this.closeDropdownGlobally = this.closeDropdownGlobally.bind(this);
            this.handleCloseDropdownGlobally =
                this.handleCloseDropdownGlobally.bind(this);
            this.dropdownKeyboardNavigation =
                this.dropdownKeyboardNavigation.bind(this);
            this.handleToggleDropdown = this.handleToggleDropdown.bind(this);
        }
    
        // Store breadcrumb references
        store() {
            // Attempt to grab all breadcrumbs...
            const elements = this.references.length
                ? this.references
                : Array.from(document.querySelectorAll(this.selectors.breadcrumb));
            // ... and if none exist, do nothing else
            if (!elements.length) return;
    
            // Finally, store the references
            this.references = elements.map((element) => {
                // If the reference is already an object, return the reference as is
                if (element.element) return element;
    
                // Otherwise, grab the breadcrumb's direct links...
                const links = Array.from(
                    element.querySelectorAll(
                        [
                            ':scope',
                            this.selectors.list,
                            this.selectors.item,
                            this.selectors.link
                        ].join('>')
                    )
                );
                // ... and create a reference object from the element and its links
                let reference = {
                    element,
                    links
                };
    
                // Next, check to see if a dropdown parent item exists...
                const parent = element.querySelector(
                    this.selectors.itemHasDropdown
                );
                // ... and if doesn't, return the reference object as-is
                if (!parent) return reference;
    
                // Otherwise, set the reference's expanded flag to false...
                reference.expanded = false;
                // ... and add a dropdown key object consisting of...
                reference.dropdown = {
                    // ... the dropdown parent item,...
                    parent,
                    // ... the toggle,...
                    toggle: parent.querySelector(this.selectors.toggle),
                    // ... the dropdown list,...
                    list: parent.querySelector(this.selectors.listIsDropdown),
                    // ... and the dropdown links
                    links: Array.from(parent.querySelectorAll(this.selectors.link))
                };
                // ... and return the reference
                return reference;
            });
        }
    
        // Returns a reference containing the given element
        getReferenceOf(element, asArray = false) {
            // If the element is a boolean, return all references...
            if (typeof element === 'boolean') return this.references;
            // ... otherwise, find the reference containing the given element
            const match = this.references.find((reference) => {
                return (
                    reference.links.includes(element) ||
                    (reference.dropdown
                        ? reference.dropdown.toggle === element ||
                          reference.dropdown.links.includes(element)
                        : false)
                );
            });
    
            // Finally, if no match was found, return an appropriate nullish value...
            if (!match) return asArray ? [] : match;
            // ... or return the match in an array or by itself
            return asArray ? [match] : match;
        }
    
        // Breadcrumb keyboard navigation
        breadcrumbKeyboardNavigation(event) {
            // Grab the type and current target from the event
            const { type, currentTarget } = event;
    
            // Handle whether to add/remove a keydown event listener based on the event type...
            switch (type) {
                case 'focus': // Focus = add keydown event listener
                    currentTarget.addEventListener(
                        'keydown',
                        this.breadcrumbKeyboardNavigation
                    );
                    break;
                case 'blur': // Blur = remove keydown event listener
                    currentTarget.removeEventListener(
                        'keydown',
                        this.breadcrumbKeyboardNavigation
                    );
            }
            // ... and if this isn't a keydown event, do nothing else
            if (type !== 'keydown') return;
    
            // Grab the key from the event...
            const { key } = event;
            // ... and if the key isn't home, left/right arrows, or end, do nothing else
            if (!['Home', 'ArrowLeft', 'ArrowRight', 'End'].includes(key)) return;
    
            // Prevent the default action
            event.preventDefault();
    
            // Attempt to grab the reference containing the current target,...
            const reference = this.getReferenceOf(currentTarget);
            // ... and if doesn't exist, do nothing else
            if (!reference) return;
    
            // Otherwise, create a list of focusable elements,...
            const focusable = [
                reference.dropdown ? reference.dropdown.toggle : null,
                ...reference.links
            ].filter(Boolean);
            // ... and figure out the first/last focusable element as well as the currently focused element's index
            const firstIndex = 0,
                firstFocusable = focusable[firstIndex],
                index = focusable.indexOf(currentTarget),
                lastIndex = focusable.length - 1,
                lastFocusable = focusable[lastIndex],
                isOnFirst = index === firstIndex,
                isOnLast = index === lastIndex;
    
            // Handle what to do based on what key was pressed
            switch (key) {
                case 'Home': // Home = Focus the first element
                    return firstFocusable.focus();
                case 'ArrowLeft': // Left arrow = Focus the previous element, looping around to the last element
                    return isOnFirst
                        ? lastFocusable.focus()
                        : focusable[index - 1].focus();
                case 'ArrowRight': // Right arrow = Focus the next element, looping around the first element
                    return isOnLast
                        ? firstFocusable.focus()
                        : focusable[index + 1].focus();
                case 'End': // End = Focus the last element
                    return lastFocusable.focus();
            }
        }
    
        // Handles generic focus/blur events
        handleFocusBlurEvents(element, listener) {
            element[this.listener]('focus', listener, false);
            element[this.listener]('blur', listener, false);
        }
    
        // Handle breadcrumb keyboard navigation
        handleBreadcrumbKeyboardNavigation(element) {
            this.handleFocusBlurEvents(element, this.breadcrumbKeyboardNavigation);
        }
    
        // Close the dropdown globally
        closeDropdownGlobally({ type, target }, { dropdown }) {
            // If the reference doesn't have a dropdown, do nothing else
            if (!dropdown) return;
    
            // Grab all dropdown references....
            const { parent, toggle, list, links } = dropdown;
            // ... and create a list of valid targets
            let targets = [
                parent,
                toggle,
                ...Array.from(toggle.children),
                list,
                ...links
            ];
    
            // Add all link children to the list of valid targets
            links.forEach(({ children }) => {
                targets.push(...children);
            });
    
            // If this is a click event and one of the valid targets were clicked, do nothing else
            if (type === 'click' && targets.includes(target)) return;
    
            // Otherwise, close the dropdown
            return this.handleToggleDropdown(false);
        }
    
        handleCloseDropdownGlobally(event) {
            // Grab the type and key from the event...
            const { type, key } = event;
            // ... and if the type is keydown but the key is not "Escape", do nothing else
            if (type === 'keydown' && key !== 'Escape') return;
    
            // Otherwise, close the dropdown for each current reference
            this.current.forEach(this.closeDropdownGlobally.bind(this, event));
        }
    
        // Toggle a dropdown open/close
        toggleDropdown(event, reference) {
            // If the reference doesn't have a dropdown, do nothing else
            if (!reference.dropdown) return;
    
            // Set the reference's expanded flag
            reference.expanded =
                typeof event === 'boolean' ? event : !reference.expanded;
    
            // Get the dropdown parent item
            const { parent } = reference.dropdown;
    
            // Add/remove the expanded class to the dropdown parent based on the expanded state
            parent.classList.toggle(
                this.classes.itemExpandDropdown,
                reference.expanded
            );
    
            // When an animation frame is available...
            window.requestAnimationFrame(() => {
                // If the event was a click, add a global keydown event to close the dropdown
                if (event.detail)
                    document[super.stateListener(reference.expanded)](
                        'keydown',
                        this.handleCloseDropdownGlobally
                    );
    
                // Add a global click event to close the dropdown
                document[super.stateListener(reference.expanded)](
                    'click',
                    this.handleCloseDropdownGlobally
                );
            });
    
            // If the event was a click...
            if (event.detail) return;
            // ... or the dropdown has been collapsed, do nothing else
            if (!reference.expanded) return;
    
            // Grab all the dropdown links...
            const { links } = reference.dropdown;
            // ... and focus the first one
            links[0].focus();
        }
    
        // Handle dropdown toggling
        handleToggleDropdown(event) {
            // Store whether the event is forced,...
            const forced = typeof event === 'boolean';
            // ... get the reference,...
            const references = this.getReferenceOf(
                forced || event.currentTarget,
                true
            );
            // ... and if the reference doesn't exist, do nothing else
            if (!references || !references.length) return;
    
            // Globally store the current references...
            this.current = references;
            // ... and toggle the dropdown for each reference
            references.forEach(this.toggleDropdown.bind(this, event));
        }
    
        // Dropdown keyboard navigation
        dropdownKeyboardNavigation(event) {
            // Grab the type and current target from the event
            const { type, currentTarget } = event;
    
            // Handle whether to add/remove a keydown event listener based on the event type...
            switch (type) {
                case 'focus': // Focus = add keydown event listener
                    currentTarget.addEventListener(
                        'keydown',
                        this.dropdownKeyboardNavigation
                    );
                    break;
                case 'blur': // Blur = remove keydown event listener
                    currentTarget.removeEventListener(
                        'keydown',
                        this.dropdownKeyboardNavigation
                    );
            }
            // ... and if this isn't a keydown event, do nothing else
            if (type !== 'keydown') return;
    
            // Attempt to grab the reference of the current target...
            const reference = this.getReferenceOf(currentTarget);
            // ... and if none were found, do nothing else
            if (!reference) return;
    
            // Grab the key from the event...
            const { key } = event;
            // ... and if it's escape,...
            if (key === 'Escape') {
                // ... close the dropdown,...
                this.handleToggleDropdown(false);
                // ... and focus the dropdown toggle
                return reference.dropdown.toggle.focus();
            }
    
            // If the key isn't home, up/down arrows, or end, do nothing else
            if (!['Home', 'ArrowUp', 'ArrowDown', 'End'].includes(key)) return;
    
            // Prevent the default action
            event.preventDefault();
    
            // Grab the dropdown links...
            const { links } = reference.dropdown;
            // ... and figure out...
            const firstIndex = 0;
            // ... the first...
            const firstLink = links[firstIndex];
            const index = links.indexOf(currentTarget);
            const lastIndex = links.length - 1;
            // ... and last links...
            const lastLink = links[lastIndex];
            // ... as well as the currently focused link's index
            const isOnFirst = index === firstIndex;
            const isOnLast = index === lastIndex;
    
            // Handle what to do based on what key was pressed
            switch (key) {
                case 'Home': // Home = Focus the first link
                    return firstLink.focus();
                case 'ArrowUp': // Up arrow = Focus the previous link, looping around to the last link
                    return isOnFirst ? lastLink.focus() : links[index - 1].focus();
                case 'ArrowDown': // Down arrow = Focus the previous, looping around the first link
                    return isOnLast ? firstLink.focus() : links[index + 1].focus();
                case 'End': // End = Focus the last link
                    return lastLink.focus();
            }
        }
    
        // Handle dropdown keyboard navigation
        handleDropdownKeyboardNavigation(element) {
            this.handleFocusBlurEvents(element, this.dropdownKeyboardNavigation);
        }
    
        // Handle breadcrumb functionality
        functionality() {
            // For each reference,...
            this.references.forEach(({ links, dropdown }) => {
                // ... handle breadcrumb keyboard navigation on the direct links...
                links.forEach(this.handleBreadcrumbKeyboardNavigation.bind(this));
                // ... and if it doesn't have a dropdown, do nothing else
                if (!dropdown) return;
    
                const { toggle, links: dropdownLinks } = dropdown;
    
                // Otherwise, handle dropdown toggling when the toggle is clicked,...
                toggle[this.listener]('click', this.handleToggleDropdown, false);
                // ... handle breadcrumb keyboard navigation on the dropdown toggle,...
                this.handleBreadcrumbKeyboardNavigation(toggle);
                // ... and handle dropdown keyboard navigation on the dropdown links
                dropdownLinks.forEach(
                    this.handleDropdownKeyboardNavigation.bind(this)
                );
            });
        }
    
        // Mount/unmount the breadcrumb functionality
        mount(run) {
            super.mount(run);
    
            // If mounting, store breadcrumb references...
            if (run) this.store();
            // ... and handle their functionality
            this.functionality();
    
            // If unmounting, delete the breadcrumb references
            if (!run) this.references = [];
        }
    }
    
  • URL: /components/raw/breadcrumb/index.js
  • Filesystem Path: components/breadcrumb/index.js
  • Size: 15.7 KB

Breadcrumb

The breadcrumb component is a tertiary navigation that helps users understand where in the page structure they are currently at. Inspiration taken from the WAI Aria Practices 1.1 breadcrumb example.

Usage

The breadcrumb navigation is automatically included in the page layout, and is placed within the <main> column above all page content.

Structure & styles

The breadcrumb component can be rendered differently depending on how deep within the page structure it’s being used. The styles will also change on mobile viewports to more cleanly display the information.

Regular

By default, the breadcrumb will have links to the parent page or the grandparent and parent pages, and the name of the current page, horizontally and separated by forward slashes. On smaller viewports, the parent page will be displayed as back button to the left, and if a grandparent page is included, it will displayed to the right (the homepage will be styled as a home icon to preserve space).

Truncated

When the current page is 4 or more levels deep, all pages higher than the parent page will be truncated within a “More” menu. Clicking on the “More” button will open up the menu showing all pages higher than the parent page. On smaller viewports, this button/menu will be displayed to the right as a 3 vertical dots icon.

Javascript

All breadcrumb navigations support custom keyboard navigation:

  • Home - Focuses the first link within the breadcrumb
  • Left arrow - Focuses the previous link within the breadcrumb, looping back to the last link.
  • Right arrow - Focuses the next link within the breadcrumb, looping back to the first link.
  • End - Focuses the last link with the breadcrumb.

If the breadcrumb is truncated, and focus is with the “More” menu, the following custom keyboard navigation is available:

  • Home - Focuses the first link within the menu
  • Up arrow - Focuses the previous link within the menu, looping back to the last link of the menu.
  • Down arrow - Focuses the next link within the menu, looping back to the first link of the menu.
  • End - Focuses the last link with the menu.

T4 implementation

The breadcrumb component is not tied to a specific content type. Its styles are included in the Compass T4 page layout CSS, and is used by the page layout at the top of the main column on any page except the homepage. The breadcrumb will automatically populate and update based on your site’s section structure.