Créer un composant de fil d'Ariane

Présentation de base sur la création d'un composant de fil d'Ariane responsif et accessible permettant aux utilisateurs de naviguer sur votre site.

Dans cet article, je vais vous présenter une façon de créer des composants de fil d'Ariane. Tester la fonctionnalité

Démo

Si vous préférez regarder une vidéo, voici une version YouTube de cet article:

Présentation

Un composant fil d'Ariane indique l'emplacement de l'utilisateur dans la hiérarchie du site. Ce nom vient de Hansel et Gretel, qui ont laissé des miettes de pain derrière eux dans une forêt sombre et ont pu retrouver leur chemin en suivant les miettes en arrière.

Les éléments de navigation de cet article ne sont pas des éléments de navigation standards, mais des éléments de navigation similaires. Ils offrent des fonctionnalités supplémentaires en insérant des pages sœurs directement dans la navigation avec un <select>, ce qui permet un accès à plusieurs niveaux.

Expérience utilisateur en arrière-plan

Dans la vidéo de démonstration du composant ci-dessus, les catégories d'espaces réservés sont des genres de jeux vidéo. Pour créer ce chemin, accédez à home » rpg » indie » on sale, comme illustré ci-dessous.

Ce composant de fil d'Ariane doit permettre aux utilisateurs de parcourir cette hiérarchie d'informations, en sautant des branches et en sélectionnant des pages rapidement et précisément.

Architecture de l'information

Je trouve utile de penser en termes de collections et d'éléments.

Collections

Une collection est un tableau d'options parmi lesquelles choisir. Sur la page d'accueil du prototype de fil d'Ariane de cet article, les collections sont les suivantes : FPS, RPG, bagarre, jeu de rôle, sport et puzzle.

Éléments

Un jeu vidéo est un élément. Une collection spécifique peut également être un élément si elle représente une autre collection. Par exemple, "RPG" est un élément et une collection valide. S'il s'agit d'un article, l'utilisateur se trouve sur la page de cette collection. Par exemple, elles se trouvent sur la page des jeux de rôle, qui affiche une liste de jeux de rôle, y compris les sous-catégories supplémentaires AAA, indépendants et auto-publiés.

En termes d'informatique, ce composant de fil d'Ariane représente un tableau multidimensionnel:

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

Votre application ou votre site Web aura une architecture de l'information (AI) personnalisée qui créera un tableau multidimensionnel différent, mais j'espère que le concept de pages de destination de collection et de parcours hiérarchique pourra également être intégré à vos miettes de pain.

Mises en page

Annoter

Les bons composants commencent par du code HTML approprié. Dans la section suivante, je vais décrire mes choix de balisage et leur impact sur le composant global.

Thèmes sombre et clair

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

La balise méta color-scheme de l'extrait ci-dessus informe le navigateur que cette page souhaite les styles de navigateur clair et sombre. Les exemples de fil d'Ariane n'incluent aucun CSS pour ces jeux de couleurs. Ils utiliseront donc les couleurs par défaut fournies par le navigateur.

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

Il est approprié d'utiliser l'élément <nav> pour la navigation sur le site, qui a un rôle ARIA implicite de navigation. Lors des tests, j'ai remarqué que l'attribut role modifiait la façon dont un lecteur d'écran interagit avec l'élément. Il était en fait annoncé comme navigation. C'est pourquoi j'ai choisi de l'ajouter.

Icônes

Lorsqu'une icône est répétée sur une page, l'élément SVG <use> vous permet de définir path une fois et de l'utiliser pour toutes les instances de l'icône. Cela évite la répétition des mêmes informations de chemin d'accès, ce qui entraîne des documents plus volumineux et une incohérence potentielle du chemin d'accès.

Pour utiliser cette technique, ajoutez un élément SVG masqué à la page et encapsulez les icônes dans un élément <symbol> avec un ID unique:

<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>

Le navigateur lit le code HTML SVG, met les informations de l'icône en mémoire et poursuit le reste de la page en référençant l'ID pour d'autres utilisations de l'icône, comme suit:

<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 affichant un élément d&#39;utilisation SVG affiché

Définissez une fois, utilisez autant de fois que vous le souhaitez, avec un impact minimal sur les performances de la page et un style flexible. Notez que aria-hidden="true" est ajouté à l'élément SVG. Les icônes ne sont pas utiles à un utilisateur qui ne voit que le contenu audio. En les masquant, vous évitez qu'il ajoute du bruit inutile.

C'est là que le fil d'Ariane traditionnel et celui de ce composant divergent. Normalement, il ne s'agit que d'un lien <a>, mais j'ai ajouté une expérience utilisateur de navigation avec une sélection masquée. La classe .crumb est chargée de mettre en page le lien et l'icône, tandis que .crumbicon est chargé d'empiler l'icône et l'élément de sélection ensemble. J'ai appelé ce lien "lien fractionné", car ses fonctions sont très similaires à celles d'un bouton fractionné, mais pour la navigation entre les pages.

<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>

Un lien et quelques options ne sont rien de spécial, mais ils ajoutent plus de fonctionnalités à un simple fil d'Ariane. Ajouter un title à l'élément <select> est utile pour les utilisateurs de lecteurs d'écran, car il leur fournit des informations sur l'action du bouton. Toutefois, il fournit la même aide à tous les autres utilisateurs. Vous le verrez en plein écran sur l'iPad. Un seul attribut fournit le contexte du bouton à de nombreux utilisateurs.

Capture d&#39;écran montrant l&#39;élément de sélection invisible survolé et son info-bulle contextuelle affichée.

Décorations de séparateurs

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

Les séparateurs sont facultatifs. Vous pouvez n'en ajouter qu'un seul (voir le troisième exemple dans la vidéo ci-dessus). Je donne ensuite à chaque aria-hidden="true", car ils sont décoratifs et ne sont pas des éléments qu'un lecteur d'écran doit annoncer.

La propriété gap, qui sera abordée plus loin, permet de définir facilement l'espacement de ces éléments.

Styles

Étant donné que la couleur utilise des couleurs système, les styles sont principalement constitués d'espaces et de piles.

Orientation et flux de la mise en page

DevTools affichant l&#39;alignement de la navigation par fil d&#39;Ariane avec la fonctionnalité de superposition flexbox.

L'élément de navigation principal nav.breadcrumbs définit une propriété personnalisée de portée pour les enfants et établit une mise en page horizontale alignée verticalement. Cela garantit que les miettes de pain, les séparateurs et les icônes s'alignent.

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

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

Une seule piste de navigation affichée verticalement avec des superpositions flexbox.

Chaque .crumb établit également une mise en page horizontale alignée verticalement avec un espacement, mais cible spécifiquement ses enfants de lien et spécifie le style white-space: nowrap. Cela est crucial pour les fil d'Ariane multi-mots, car nous ne voulons pas qu'ils s'affichent sur plusieurs lignes. Plus loin dans cet article, nous ajouterons des styles pour gérer le débordement horizontal causé par cette propriété white-space.

.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" est ajouté pour que le lien de la page actuelle se démarque des autres. Les utilisateurs de lecteurs d'écran disposent d'un indicateur clair indiquant que le lien concerne la page actuelle. Nous avons également stylisé l'élément visuellement pour aider les utilisateurs voyants à bénéficier d'une expérience utilisateur similaire.

Le composant .crumbicon utilise une grille pour empiler une icône SVG avec un élément <select> "presque invisible".

Les outils de développement de la grille sont superposés à un bouton où la ligne et la colonne sont toutes deux nommées &quot;pile&quot;.

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

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

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

L'élément <select> est le dernier du DOM. Il se trouve donc au sommet de la pile et est interactif. Ajoutez un style opacity: .01 pour que l'élément reste utilisable. Vous obtenez ainsi une zone de sélection qui s'adapte parfaitement à la forme de l'icône. C'est un bon moyen de personnaliser l'apparence d'un élément <select> tout en conservant la fonctionnalité intégrée.

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

Dépassement

Les éléments de fil d'Ariane doivent pouvoir représenter un chemin très long. J'aime laisser les éléments sortir de l'écran horizontalement, le cas échéant, et je pense que ce composant de fil d'Ariane s'y prête bien.

.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;
  }}
}

Les styles de débordement configurent l'expérience utilisateur suivante:

  • Défilement horizontal avec structuration du défilement hors limites.
  • Marge intérieure horizontale.
  • Un point d'ancrage sur la dernière miette. Cela signifie que lors du chargement de la page, le premier maillon se charge de manière fixe et visible.
  • Supprime le point d'ancrage de Safari, qui a du mal à gérer les combinaisons de défilement horizontal et d'effet d'ancrage.

Requêtes multimédias

Pour les vues d'affichage plus petites, vous pouvez masquer l'étiquette "Accueil" et ne conserver que l'icône:

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

Comparaison côte à côte des éléments de navigation avec et sans étiquette &quot;Accueil&quot;.

Accessibilité

Mouvement

Il n'y a pas beaucoup de mouvement dans ce composant, mais en encapsulant la transition dans une vérification prefers-reduced-motion, nous pouvons éviter les mouvements indésirables.

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

Aucun des autres styles ne doit être modifié. Les effets de survol et de mise au point sont excellents et pertinents sans transition, mais si le mouvement est acceptable, nous ajouterons une transition subtile à l'interaction.

JavaScript

Tout d'abord, quel que soit le type de routeur que vous utilisez sur votre site ou dans votre application, lorsqu'un utilisateur modifie les miettes de pain, l'URL doit être mise à jour et la page appropriée doit s'afficher. Deuxièmement, pour normaliser l'expérience utilisateur, assurez-vous qu'aucune navigation inattendue ne se produit lorsque les utilisateurs ne font que parcourir les options <select>.

Deux mesures essentielles de l'expérience utilisateur à gérer par JavaScript: la sélection a changé et la prévention du déclenchement d'événements de modification <select> rapide.

La prévention des événements avides est nécessaire en raison de l'utilisation d'un élément <select>. Dans Windows Edge et probablement dans d'autres navigateurs, l'événement de sélection changed se déclenche lorsque l'utilisateur parcourt les options avec le clavier. C'est pourquoi je l'ai appelé "eager", car l'utilisateur n'a que pseudo-sélectionné l'option, comme un survol ou un focus, mais n'a pas confirmé le choix avec enter ou click. L'événement "eager" rend cette fonctionnalité de modification de la catégorie de composants inaccessible, car l'ouverture de la zone de sélection et la simple navigation sur un élément déclenchent l'événement et modifient la page avant que l'utilisateur ne soit prêt.

Amélioration de l'événement de modification <select>

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
  })
})

La stratégie consiste à surveiller les événements de clavier enfoncé sur chaque élément <select> et à déterminer si la touche enfoncée était une confirmation de navigation (Tab ou Enter) ou une navigation spatiale (ArrowUp ou ArrowDown). Avec cette détermination, le composant peut décider d'attendre ou de continuer lorsque l'événement de l'élément <select> se déclenche.

Conclusion

Maintenant que vous savez comment j'ai fait, comment procéderiez-vous ? 🙂

Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez une démo, tweetez-moi des liens et je les ajouterai à la section "Remix de la communauté" ci-dessous.

Remix de la communauté