City of Ghent Style Guide

Breadcrumbs

The breadcrumbs component helps users understand where they are in the website’s structure and to let them move between levels of the website.

When to use this component

Use the breadcrumbs component when you need to help users understand and move between the multiple levels of a website.

Breadcrumbs are required for most websites.

When breadcrumbs are used, breadcrumbs should be included:

  • On all pages of the website, except the home page.
  • On all device resolutions (desktop, tablet and mobile).

When not to use this component

Do not use the breadcrumbs component on websites that have a flat website structure, in other words, websites that go only one level deep. In this case, breadcrumbs can be omitted.

Do not use the breadcrumbs component to show progress or a linear flow when collecting user input in multiple steps or to guide users through a process. In these cases, use a multistep form or a wizard instead.

How it works

Breadcrumbs show website users their current location relative to higher-level sections. They also afford one-click access to higher website sections and to the homepage of the website.

Breadcrumbs are composed as follows:

  1. They start with a link to the homepage. This link always links to the homepage of the current website where the website user is (see some examples below). This link should always have the label “Home”.
  2. After that the higher-level sections relative to the current web page are shown using links to each of these sections.
  3. They end with the page title of the current page. This is not a link.

Examples for the link to the homepage:

  • On the City of Ghent general website, including theme sites, the breadcrumbs starts with “Home” and links to the homepage of the City of Ghent general website.
  • On a separate website, the breadcrumbs also starts with “Home” and links to the corresponding homepage of that separate website.

Accessibility

  • Breadcrumbs must be contained in a nav region.
  • The nav region must include a visually hidden title ‘breadcrumbs’ (translatable) as first element.
  • The nav region must use the aria-labelledby attribute to reference the hidden title.
<nav class="breadcrumb section--default" aria-labelledby="{{ id|default('system-breadcrumb--' ~ _self.name) }}">
  <div class="content-container">
    <h2 id="{{ id|default('system-breadcrumb--' ~ _self.name) }}" class="visually-hidden">{{ 'Breadcrumb' }}</h2>
    <ol class="no-style">
      {% apply spaceless %}
        {% for item in items %}
          <li>{{ item }}</li>
        {% endfor %}
      {% endapply %}
    </ol>
  </div>
</nav>
<nav class="breadcrumb section--default" aria-labelledby="system-breadcrumb--extremely-long">
    <div class="content-container">
        <h2 id="system-breadcrumb--extremely-long" class="visually-hidden">Breadcrumb</h2>
        <ol class="no-style">
            <li><a href="">Home</a href=""></li>
            <li><a href="">Item 1</a href=""></li>
            <li><a href="">Item 2</a href=""></li>
            <li><a href="">Item 3</a href=""></li>
            <li><a href="">Item 4</a href=""></li>
            <li><a href="">Item 5</a href=""></li>
            <li><a href="">Item 6</a href=""></li>
            <li><a href="">Item 7</a href=""></li>
            <li><a href="">Item 8</a href=""></li>
            <li><a href="">Item 9</a href=""></li>
            <li><a href="">Item 10</a href=""></li>
            <li><a href="">Item 11</a href=""></li>
            <li><a href="">Item 12</a href=""></li>
            <li><a href="">Item 13</a href=""></li>
            <li><a href="">Item 14</a href=""></li>
            <li><a href="">Item 15</a href=""></li>
            <li><a href="">Item 16</a href=""></li>
            <li><a href="">Item 17</a href=""></li>
            <li><a href="">Item 18</a href=""></li>
            <li><span>A very long page title with lots of words</span></li>
        </ol>
    </div>
</nav>
{
  "items": [
    "<a href=\"\">Home</a href=\"\">",
    "<a href=\"\">Item 1</a href=\"\">",
    "<a href=\"\">Item 2</a href=\"\">",
    "<a href=\"\">Item 3</a href=\"\">",
    "<a href=\"\">Item 4</a href=\"\">",
    "<a href=\"\">Item 5</a href=\"\">",
    "<a href=\"\">Item 6</a href=\"\">",
    "<a href=\"\">Item 7</a href=\"\">",
    "<a href=\"\">Item 8</a href=\"\">",
    "<a href=\"\">Item 9</a href=\"\">",
    "<a href=\"\">Item 10</a href=\"\">",
    "<a href=\"\">Item 11</a href=\"\">",
    "<a href=\"\">Item 12</a href=\"\">",
    "<a href=\"\">Item 13</a href=\"\">",
    "<a href=\"\">Item 14</a href=\"\">",
    "<a href=\"\">Item 15</a href=\"\">",
    "<a href=\"\">Item 16</a href=\"\">",
    "<a href=\"\">Item 17</a href=\"\">",
    "<a href=\"\">Item 18</a href=\"\">",
    "<span>A very long page title with lots of words</span>"
  ]
}
  • Content:
    .breadcrumb {
      margin: 0 0 20px;
      clear: both;
    
      ul,
      ol {
        flex-wrap: wrap;
        max-width: calc(100vw - 40px);
        margin: 0;
        list-style: none;
    
        li {
          @include icon('chevron-right', 'after');
    
          display: inline-flex;
          align-items: center;
          margin-right: 8px;
          margin-bottom: 0;
          padding-left: 0;
    
          &::before {
            display: none;
          }
    
          &::after {
            @include theme('color', 'color-primary', 'breadcrumb-arrows-color');
    
            margin: 0 0 0 8px;
          }
    
          &:last-of-type {
            margin-right: 0;
    
            &::after {
              display: none;
              content: '';
            }
          }
    
          > * {
            @include small-text;
          }
    
          a {
            @include normal-text;
            @include small-text;
    
            line-height: initial;
    
            &,
            &[href^="http://"],
            &[href^="https://"] {
              &:not(.no-icon):not(.tag) {
                &::after {
                  display: none;
                }
              }
            }
          }
    
          a,
          span {
            display: inline-block;
          }
        }
    
        &.nav--mobile-breadcrumb {
          display: block;
    
          @media (min-width: $bp-tablet) {
            display: none;
          }
        }
      }
    
      .expandable {
        a {
          @include bold-text;
    
          padding: 0 7px;
          border-color: transparent;
          outline: 2px solid currentColor;
          line-height: 19px;
    
          &:hover,
          &:focus {
            @include theme('background-color', 'color-primary--lighten-4', 'link-hover-background');
          }
    
          &,
          &:focus,
          &:hover {
            background-image: none;
          }
        }
    
        &::after {
          line-height: inherit;
        }
      }
    
      &[aria-expanded="false"] {
        ul,
        ol {
          li:not(.expandable):not(:nth-last-child(-n+3)):not(:nth-child(-n+2)) {
            display: none;
          }
    
          @include tablet-and-below {
            display: flex;
            align-items: center;
    
            li:not(.expandable) {
              min-width: 60px;
    
              a,
              span {
                text-overflow: ellipsis;
                white-space: nowrap;
                overflow: hidden;
              }
            }
    
            li:not(.expandable):not(:nth-last-child(-n+3)) {
              display: none;
            }
          }
        }
      }
    
      ///
      /// Links in the breadcrumb should never have icons.
      ///
      [class*="cs--"] & a {
        &::after {
          content: none;
        }
      }
    }
    
  • URL: /components/raw/breadcrumbs/_breadcrumbs.scss
  • Filesystem Path: components/31-molecules/breadcrumbs/_breadcrumbs.scss
  • Size: 2.4 KB
  • Content:
    'use strict';
    
    (function () {
    
      if (!Breadcrumbs) { // eslint-disable-line no-undef
        return;
      }
    
      const selected = document.querySelectorAll('.breadcrumb');
      for (let i = selected.length; i--;) {
        new Breadcrumbs(selected[i]); // eslint-disable-line no-undef
      }
    
    })();
    
  • URL: /components/raw/breadcrumbs/breadcrumbs.bindings.js
  • Filesystem Path: components/31-molecules/breadcrumbs/breadcrumbs.bindings.js
  • Size: 280 Bytes
  • Content:
    'use strict';
    
    (function (root, factory) {
      if (typeof define === 'function' && define.amd) {
        define(factory);
      }
      else {
        if (typeof exports === 'object') {
          module.exports = factory();
        }
        else {
          root.Breadcrumbs = factory();
        }
      }
    }(this || window, function () {
    
      return function (elem, options) {
        let list = elem.querySelector('ol, ul');
        let items = elem.querySelectorAll('li');
        let expandable;
    
        /**
         * Add an element to the DOM that makes it possible to expand the breadcrumb.
         *
         * @param {Int} position
         *   Index of the new element.
         */
        const insertExpandable = position => {
          let a = document.createElement('a');
          a.textContent = '...';
          a.href = '#';
    
          // attach event listener.
          a.addEventListener('click', expand);
    
          // Add it to the DOM
          expandable = document.createElement('li');
          expandable.classList.add('expandable');
          expandable.appendChild(a);
          list.insertBefore(expandable, list.children[position]);
          list.tabIndex = -1;
        };
    
        /**
         *  Collapse the breadcrumb.
         *
         * @param {MediaQueryList} query
         *   The results of the matchMedia function.
         */
        const collapse = query => {
          // Remove any old expandables.
          removeExpandable();
    
          // Collapse when more than 5 items on tablet+,
          // or more than 2 items on mobile screens
          if ((query.matches && items.length > 5) ||
          (!query.matches && items.length > 2)) {
            elem.setAttribute('aria-expanded', false);
            insertExpandable(items.length - 2);
          }
        };
    
        /**
         * Remove the expandable for the current breadcrumb.
         */
        const removeExpandable = () => {
          if (expandable) {
            expandable.parentNode.removeChild(expandable);
          }
        };
    
        /**
         * Expand the entire breadcrumb.
         *
         * @param {Event} e
         *   Event object.
         */
        const expand = e => {
          removeExpandable();
          elem.setAttribute('aria-expanded', true);
          list.focus();
        };
    
        /**
         * Initialize functionality.
         */
        const init = () => {
          let query = window.matchMedia('(min-width: 768px)');
          collapse(query); // Execute once on page load.
          query.addListener(collapse);
        };
    
        init();
    
        return {};
      };
    }));
    
  • URL: /components/raw/breadcrumbs/breadcrumbs.functions.js
  • Filesystem Path: components/31-molecules/breadcrumbs/breadcrumbs.functions.js
  • Size: 2.3 KB