Breadcrumbs-Komponente erstellen

Eine grundlegende Übersicht dazu, wie Sie eine responsive und barrierefreie Breadcrumbs-Komponente erstellen, damit Nutzer auf Ihrer Website navigieren können.

In diesem Beitrag möchte ich meine Überlegungen zur Entwicklung von Breadcrumb-Komponenten teilen. Demo ansehen.

Demo

Wenn Sie lieber ein Video ansehen möchten, finden Sie hier eine YouTube-Version dieses Beitrags:

Übersicht

Die Komponente Navigationspfad zeigt an, wo sich der Nutzer in der Websitehierarchie befindet. Der Name stammt von Hänsel und Gretel, die in einem dunklen Wald Brotkrumen hinter sich herwarfen und so den Weg nach Hause fanden.

Die Breadcrumbs in diesem Beitrag sind keine Standard-Breadcrumbs, sondern ähneln Breadcrumbs. Sie bieten zusätzliche Funktionen, indem sie untergeordnete Seiten direkt in die Navigation mit einem <select> einfügen und so einen mehrstufigen Zugriff ermöglichen.

Hintergrund-UX

Im Demovideo für die Komponente oben sind die Platzhalterkategorien Genres von Videospielen. Dieser Pfad wird erstellt, indem Sie den folgenden Pfad aufrufen: home » rpg » indie » on sale (siehe unten).

Mit dieser Breadcrumb-Komponente sollten Nutzer schnell und präzise durch diese Informationshierarchie navigieren können, indem sie Zweige überspringen und Seiten auswählen.

Informationsarchitektur

Es ist hilfreich, sich Sammlungen und Elemente vorzustellen.

Sammlungen

Eine Sammlung ist ein Array mit Optionen, aus denen Sie auswählen können. Auf der Startseite des Breadcrumb-Prototyps dieses Beitrags sind die Sammlungen „FPS“, „RPG“, „Brawler“, „Dungeon Crawler“, „Sports“ und „Puzzle“ zu sehen.

Artikel

Ein Videospiel ist ein Artikel. Eine bestimmte Sammlung kann auch ein Artikel sein, wenn sie eine andere Sammlung repräsentiert. Beispiel: „RPG“ ist ein Element und eine gültige Sammlung. Wenn es sich um einen Artikel handelt, befindet sich der Nutzer auf der entsprechenden Sammlungsseite. Sie befinden sich beispielsweise auf der Seite für Rollenspiele, auf der eine Liste von Rollenspielen angezeigt wird, einschließlich der zusätzlichen Unterkategorien „AAA“, „Indie“ und „Selbstveröffentlicht“.

In der Informatik stellt diese Breadcrumbs-Komponente ein mehrdimensionales Array dar:

const rawBreadcrumbData = {
  "FPS": {...},
  "RPG": {
    "AAA": {...},
    "indie": {
      "new": {...},
      "on sale": {...},
      "under 5": {...},
    },
    "self published": {...},
  },
  "brawler": {...},
  "dungeon crawler": {...},
  "sports": {...},
  "puzzle": {...},
}

Ihre App oder Website hat eine benutzerdefinierte Informationsarchitektur, die ein anderes mehrdimensionales Array erstellt. Ich hoffe jedoch, dass das Konzept der Sammlungs-Landingpages und der Hierarchiedurchlauf auch in Ihren Breadcrumbs umgesetzt werden kann.

Layouts

Markieren & Zeichnen

Gute Komponenten beginnen mit dem richtigen HTML. Im nächsten Abschnitt gehe ich auf meine Markup-Entscheidungen ein und erläutere, wie sie sich auf die Gesamtkomponente auswirken.

Dunkles und helles Schema

<meta name="color-scheme" content="dark light">

Das Meta-Tag color-scheme im obigen Snippet informiert den Browser darüber, dass für diese Seite die hellen und dunklen Browserstile verwendet werden sollen. Die Beispiel-Breadcrumbs enthalten kein CSS für diese Farbschemas. Daher werden die Standardfarben des Browsers verwendet.

<nav class="breadcrumbs" role="navigation"></nav>

Für die Websitenavigation ist es angemessen, das Element <nav> zu verwenden, das eine implizite ARIA-Rolle „navigation“ hat. Beim Testen ist mir aufgefallen, dass das Attribut role die Art und Weise verändert hat, wie ein Screenreader mit dem Element interagiert. Es wurde als „Navigation“ angekündigt. Deshalb habe ich es hinzugefügt.

Symbole

Wenn ein Symbol auf einer Seite wiederholt wird, können Sie das <use>-Element von SVG verwenden, um die path einmal zu definieren und für alle Instanzen des Symbols zu verwenden. So wird verhindert, dass dieselben Pfadinformationen wiederholt werden, was zu größeren Dokumenten und potenziellen Pfadinkonsistenzen führen kann.

Fügen Sie dazu ein ausgeblendetes SVG-Element auf der Seite ein und umschließen Sie die Symbole mit einem <symbol>-Element mit einer eindeutigen ID:

<svg style="display: none;">

  <symbol id="icon-home">
    <title>A home icon</title>
    <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
  </symbol>

  <symbol id="icon-dropdown-arrow">
    <title>A down arrow</title>
    <path d="M19 9l-7 7-7-7"/>
  </symbol>

</svg>

Der Browser liest das SVG-HTML, speichert die Symbolinformationen im Arbeitsspeicher und fährt mit dem Rest der Seite fort. Dabei wird auf die ID verwiesen, wenn das Symbol noch einmal verwendet wird, z. B. so:

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-dropdown-arrow" />
</svg>

DevTools mit einem gerenderten SVG-use-Element

Einmal definieren, beliebig oft verwenden, mit minimalen Auswirkungen auf die Seitenleistung und flexibler Formatierung. Beachten Sie, dass dem SVG-Element aria-hidden="true" hinzugefügt wird. Die Symbole sind für Nutzer, die sich die Inhalte nur anhören, nicht hilfreich. Wenn sie ausgeblendet werden, wird unnötiger Lärm vermieden.

Hier unterscheiden sich die traditionellen Breadcrumbs von denen in dieser Komponente. Normalerweise wäre das nur ein <a>-Link, aber ich habe eine UX für die Navigation mit einer getarnten Auswahl hinzugefügt. Die Klasse .crumb ist für das Layout des Links und des Symbols verantwortlich, während die Klasse .crumbicon für das Stapeln des Symbols und des select-Elements zuständig ist. Ich habe ihn als „Split-Link“ bezeichnet, weil seine Funktionen einem Split-Button sehr ähnlich sind, aber für die Seitennavigation.

<span class="crumb">
  <a href="#sub-collection-b">Category B</a>
  <span class="crumbicon">
    <svg>...</svg>
    <select class="disguised-select" title="Navigate to another category">
      <option>Category A</option>
      <option selected>Category B</option>
      <option>Category C</option>
    </select>
  </span>
</span>

Ein Link und einige Optionen sind nichts Besonderes, bieten aber mehr Funktionalität als ein einfacher Breadcrumb. Wenn Sie dem <select>-Element ein title hinzufügen, erhalten Screenreader-Nutzer Informationen zur Aktion des Buttons. Es bietet aber auch allen anderen dieselbe Hilfe. Auf dem iPad ist es gut sichtbar platziert. Ein Attribut liefert vielen Nutzern Kontext für den Button.

Screenshot, auf dem mit dem Mauszeiger auf das unsichtbare Auswahlfeld gezeigt wird und der zugehörige Kontext-Tooltip angezeigt wird.

Trennzeichenverzierungen

<span class="crumb-separator" aria-hidden="true">→</span>

Trennzeichen sind optional. Es reicht auch, nur eines hinzuzufügen (siehe das dritte Beispiel im Video oben). Ich gebe dann jeweils aria-hidden="true" ein, da sie nur dekorativ sind und nicht von einem Screenreader vorgelesen werden müssen.

Die als Nächstes behandelte Eigenschaft gap erleichtert die Festlegung des Abstands.

Stile

Da für die Farbe Systemfarben verwendet werden, gibt es hauptsächlich Lücken und Stapel für Stile.

Layoutrichtung und -fluss

DevTools mit dem Flexbox-Overlay-Feature, das die Ausrichtung des Navigationspfads zeigt.

Das primäre Navigationselement nav.breadcrumbs legt eine benutzerdefinierte Eigenschaft mit Bereich für untergeordnete Elemente fest und richtet ansonsten ein horizontal vertikal ausgerichtetes Layout ein. So werden die Brotkrümel, Trennzeichen und Symbole richtig ausgerichtet.

.breadcrumbs {
  --nav-gap: 2ch;

  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  padding: calc(var(--nav-gap) / 2);
}

Ein Breadcrumb, der vertikal mit Flexbox-Overlays ausgerichtet ist.

Jedes .crumb richtet auch ein horizontal vertikal ausgerichtetes Layout mit etwas Abstand ein, zielt aber speziell auf die untergeordneten Links ab und gibt den Stil white-space: nowrap an. Das ist bei Breadcrumbs mit mehreren Wörtern wichtig, da sie nicht mehrzeilig sein sollen. Später in diesem Beitrag fügen wir Stile hinzu, um den horizontalen Überlauf zu beheben, der durch diese white-space-Eigenschaft verursacht wurde.

.crumb {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--nav-gap) / 4);

  & > a {
    white-space: nowrap;

    &[aria-current="page"] {
      font-weight: bold;
    }
  }
}

aria-current="page" wird hinzugefügt, damit sich der Link der aktuellen Seite von den anderen abhebt. Screenreader-Nutzer sehen nicht nur einen klaren Hinweis darauf, dass der Link zur aktuellen Seite führt, sondern wir haben das Element auch visuell so gestaltet, dass sehende Nutzer eine ähnliche Nutzererfahrung haben.

Die Komponente .crumbicon verwendet das Raster, um ein SVG-Symbol mit einem „fast unsichtbaren“ <select>-Element zu stapeln.

Die Grid DevTools werden über einer Schaltfläche angezeigt, bei der sowohl die Zeile als auch die Spalte den Namen „stack“ haben.

.crumbicon {
  --crumbicon-size: 3ch;

  display: grid;
  grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place-items: center;

  & > * {
    grid-area: stack;
  }
}

Das <select>-Element ist das letzte im DOM, befindet sich also oben im Stapel und ist interaktiv. Fügen Sie einen Stil von opacity: .01 hinzu, damit das Element weiterhin verwendet werden kann. Das Ergebnis ist ein Auswahlfeld, das perfekt zur Form des Symbols passt. So können Sie das Erscheinungsbild eines <select>-Elements anpassen und gleichzeitig die integrierte Funktionalität beibehalten.

.disguised-select {
  inline-size: 100%;
  block-size: 100%;
  opacity: .01;
  font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

Überlauf

Breadcrumbs sollten einen sehr langen Pfad darstellen können. Ich bin ein Fan davon, Elemente bei Bedarf horizontal aus dem Bildschirm laufen zu lassen, und ich fand, dass diese Breadcrumbs-Komponente dafür gut geeignet ist.

.breadcrumbs {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: calc(var(--nav-gap) / 2);

  & > .crumb:last-of-type {
    scroll-snap-align: end;
  }

  @supports (-webkit-hyphens:none) { & {
    scroll-snap-type: none;
  }}
}

Die Überlaufstile richten die folgende Benutzeroberfläche ein:

  • Horizontales Scrollen mit Overscroll-Begrenzung.
  • Abstand für horizontales Scrollen.
  • Ein Fangpunkt auf dem letzten Brotkrümel. Das bedeutet, dass beim Laden der Seite der erste Breadcrumb-Link sofort geladen und sichtbar ist.
  • Entfernt den Snap-Point aus Safari, da der Browser Probleme mit Kombinationen aus horizontalem Scrollen und Snap-Effekt hat.

Medienabfragen

Eine subtile Anpassung für kleinere Viewports besteht darin, das Label „Home“ auszublenden, sodass nur das Symbol angezeigt wird:

@media (width <= 480px) {
  .breadcrumbs .home-label {
    display: none;
  }
}

Nebeneinanderstellung der Breadcrumbs mit und ohne das Label „Zuhause“ zum Vergleich.

Bedienungshilfen

Bewegung

In dieser Komponente gibt es nicht viel Bewegung. Wenn wir die Übergänge jedoch in eine prefers-reduced-motion-Prüfung einfügen, können wir unerwünschte Bewegungen verhindern.

@media (prefers-reduced-motion: no-preference) {
  .crumbicon {
    transition: box-shadow .2s ease;
  }
}

Keiner der anderen Stile muss geändert werden. Die Hover- und Fokuseffekte sind auch ohne transition gut und sinnvoll. Wenn Animationen in Ordnung sind, fügen wir der Interaktion jedoch einen dezenten Übergang hinzu.

JavaScript

Unabhängig vom Typ des Routers, den Sie auf Ihrer Website oder in Ihrer Anwendung verwenden, muss die URL aktualisiert und dem Nutzer die entsprechende Seite angezeigt werden, wenn er die Breadcrumbs ändert. Zweitens sollten Sie darauf achten, dass keine unerwarteten Navigationsvorgänge stattfinden, wenn Nutzer nur <select>-Optionen durchsuchen.

Zwei wichtige Nutzerfreundlichkeitsmesswerte, die von JavaScript verarbeitet werden: „select has changed“ und „eager <select> change event firing prevention“.

Die Verhinderung von Eager-Ereignissen ist aufgrund der Verwendung eines <select>-Elements erforderlich. In Windows Edge und wahrscheinlich auch in anderen Browsern wird das Ereignis „select“ ausgelöst, wenn der Nutzer mit der Tastatur durch die Optionen navigiert.changed Deshalb habe ich es als „eager“ bezeichnet, da der Nutzer die Option nur pseudo ausgewählt hat, z. B. durch Hovern oder Fokussieren, die Auswahl aber noch nicht mit enter oder click bestätigt hat. Das „eager“-Ereignis macht diese Funktion zum Ändern der Komponentenkategorie unzugänglich, da durch das Öffnen des Auswahlfelds und das einfache Durchsuchen eines Elements das Ereignis ausgelöst und die Seite geändert wird, bevor der Nutzer bereit ist.

Ein besserer <select>-Termin

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
  let ignoreChange = false

  nav.addEventListener('change', e => {
    if (ignoreChange) return
    // it's actually changed!
  })

  nav.addEventListener('keydown', ({ key }) => {
    if (preventedKeys.has(key))
      ignoreChange = true
    else if (allowedKeys.has(key))
      ignoreChange = false
  })
})

Die Strategie hierfür besteht darin, auf Tastendruckereignisse für jedes <select>-Element zu warten und zu ermitteln, ob die gedrückte Taste zur Navigationsbestätigung (Tab oder Enter) oder zur räumlichen Navigation (ArrowUp oder ArrowDown) verwendet wurde. Anhand dieser Informationen kann die Komponente entscheiden, ob sie warten oder fortfahren soll, wenn das Ereignis für das <select>-Element ausgelöst wird.

Fazit

Jetzt wissen Sie, wie ich es gemacht habe. Wie würden Sie vorgehen? 🙂

Wir möchten unsere Ansätze diversifizieren und alle Möglichkeiten kennenlernen, die das Web bietet. Erstelle eine Demo, schick mir einen Tweet mit den Links und ich füge sie unten im Bereich „Community-Remixe“ hinzu.

Community-Remixe