Créer un composant de fil d'Ariane

Découvrez comment créer un composant "fil d'Ariane" réactif et accessible pour que les utilisateurs puissent parcourir votre site.

Dans ce message, je vais vous expliquer comment créer des composants de fil d'Ariane. Tester la fonctionnalité

Démonstration

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

Présentation

Un composant fil d'Ariane indique où se trouve l'utilisateur dans la hiérarchie du site. Ce nom provient de Hansel et Gretel, qui ont laissé un fil d'Ariane derrière eux dans une forêt sombre et ont pu retrouver leur chemin vers la maison en traçant des miettes en arrière.

Les fils d'Ariane de cet article ne sont pas des fils d'Ariane standards, ils ressemblent à des fils d'Ariane. Elles offrent des fonctionnalités supplémentaires en plaçant des pages frères 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'espace réservé correspondent à des genres de jeux vidéo. Ce sentier a été créé en suivant le chemin suivant: home » rpg » indie » on sale, comme indiqué ci-dessous.

Ce composant de fil d'Ariane doit permettre aux utilisateurs de se déplacer dans cette hiérarchie des informations, en sautant de branches et en sélectionnant des pages rapidement et avec précision.

Architecture de l’information

Il est utile de réfléchir 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, Brawler, Sport et Puzzle.

Articles

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, un 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, ils se trouvent sur la page du jeu de rôle, qui affiche une liste de jeux de rôle, y compris les sous-catégories supplémentaires "AAA", "Indie" et "Auto-publié".

En 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 disposera d'une architecture de l'information (AI) personnalisée créant un tableau multidimensionnel différent, mais j'espère que le concept de pages de destination de collection et de traversée de hiérarchie pourra également figurer dans votre fil d'Ariane.

Mises en page

Markup

Un bon composant commence par le code HTML approprié. Dans cette section, je vais aborder mes options de balisage et leur impact sur le composant global.

Thème sombre et clair

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

La balise Meta color-scheme dans l'extrait ci-dessus informe le navigateur que cette page souhaite utiliser les styles de navigateur clair et sombre. L'exemple de fil d'Ariane n'inclut aucun code CSS pour ces jeux de couleurs. Il utilisera 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 possède un rôle de navigation ARIA implicite. Lors des tests, j'ai remarqué que l'attribut role avait modifié la façon dont un lecteur d'écran interagissait avec l'élément. Il était en fait annoncé en tant que navigation. J'ai donc choisi de l'ajouter.

Icônes

Lorsqu'une icône est répétée sur une page, l'élément SVG <use> signifie que vous pouvez définir path une seule fois et l'utiliser pour toutes les instances de l'icône. Cela empêche la répétition des mêmes informations de chemin d'accès, ce qui entraîne des documents plus volumineux et un risque d'incohérence dans les chemins 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, place les informations de l'icône en mémoire et continue avec le reste de la page en référence à l'ID pour des utilisations supplémentaires de l'icône, comme ceci:

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

Outils de développement affichant un élément d&#39;utilisation SVG

Définissez une seule définition et utilisez-le autant de fois que vous le souhaitez, avec un impact minimal sur les performances des pages et un style flexible. Notez que aria-hidden="true" a été ajouté à l'élément SVG. Les icônes ne sont pas utiles pour une personne qui navigue sur Internet et qui n'entend que le contenu. Si vous les masquez, les utilisateurs n'ajouteront pas de bruit superflu.

C'est là que le fil d'Ariane traditionnel et ceux de ce composant divergent. Normalement, il ne s'agirait que d'un lien <a>, mais j'ai ajouté l'expérience utilisateur de balayage avec une sélection dissimulée. La classe .crumb est responsable de la mise en page du lien et de l'icône, tandis que .crumbicon se charge d'empiler l'icône et l'élément de sélection. Je l'ai appelé "split-link", car ses fonctions sont très semblables à celles d'un split-button, mais pour la navigation sur 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 certaines options n'ont rien de spécial, mais ils ajoutent des fonctionnalités à un simple fil d'Ariane. L'ajout d'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. Cependant, il offre la même aide à tous les autres utilisateurs, et vous le verrez au premier plan sur l'iPad. Un attribut fournit le contexte du bouton à de nombreux utilisateurs.

Capture d&#39;écran avec l&#39;élément de sélection invisible en train d&#39;être survolé et son info-bulle contextuelle affichée

Décorations du séparateur

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

Les séparateurs sont facultatifs, mais il est très bien d'en ajouter un (voir le troisième exemple de la vidéo ci-dessus). J'attribue ensuite chaque aria-hidden="true", car il s'agit d'un élément décoratif et non d'un élément qu'un lecteur d'écran doit annoncer.

La propriété gap, abordée ci-après, permet de simplifier l'espacement entre ces éléments.

Styles

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

Sens et flux de la mise en page

Outils de développement affichant l&#39;alignement du fil d&#39;Ariane avec sa fonctionnalité de superposition Flexbox

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

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

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

Un fil d&#39;Ariane affiché verticalement avec des superpositions Flexbox.

Chaque .crumb établit également une mise en page horizontale alignée verticalement avec un certain espace, mais cible spécifiquement ses liens enfants et spécifie le style white-space: nowrap. C'est essentiel pour les fils d'Ariane de plusieurs mots, car nous ne voulons pas qu'ils soient multilignes. Plus tard dans cet article, nous ajouterons des styles pour gérer le dépassement 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 permettre au lien de la page actuelle de se démarquer des autres. Non seulement les utilisateurs de lecteurs d'écran auront un indicateur clair que le lien concerne la page actuelle, mais nous avons également stylisé l'élément visuellement pour aider les personnes voyantes à 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".

Outils pour les développeurs Grid affichés en superposition sur un bouton où la ligne et la colonne correspondent à une pile nommée.

.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 élément du DOM. Il se trouve donc au-dessus de la pile et est interactif. Ajoutez un style opacity: .01 pour que l'élément puisse toujours être utilisé et obtenir une zone de sélection qui s'adapte parfaitement à la forme de l'icône. Il s'agit d'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

Un fil d'Ariane doit pouvoir représenter une très longue piste. J'aime beaucoup permettre aux éléments de sortir horizontalement, le cas échéant, et j'ai senti que ce composant de fil d'Ariane était bien qualifié.

.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 défilement hors limites.
  • Marge intérieure de défilement horizontal.
  • Un point d'ancrage sur la dernière miette. Ainsi, lors du chargement de la page, le premier chargement est ancré et visible.
  • Supprime le point d'ancrage de Safari, qui a du mal à combiner le défilement horizontal et l'effet d'ancrage.

Requêtes média

Pour les fenêtres d'affichage de petite taille, un ajustement subtil consiste à masquer le libellé "Accueil" en ne laissant que l'icône:

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

Côte à côte des fils d&#39;Ariane avec et sans libellé Home, à des fins de comparaison.

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 tout mouvement indésirable.

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

Aucun des autres styles n'a besoin de changer. Les effets de survol et de focus sont excellents et significatifs 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 le fil d'Ariane, l'URL doit être mise à jour et l'utilisateur doit afficher la page appropriée. Ensuite, pour normaliser l'expérience utilisateur, assurez-vous qu'aucune navigation inattendue ne se produit lorsque les utilisateurs se contentent de parcourir les options <select>.

Deux mesures critiques concernant l'expérience utilisateur doivent être gérées par JavaScript: select a changé et prévenez le déclenchement des événements de modification <select>.

La prévention des événements eager est nécessaire en raison de l'utilisation d'un élément <select>. Sur Windows Edge, et probablement sur d'autres navigateurs, l'événement de sélection changed se déclenche lorsque l'utilisateur parcourt les options à l'aide du clavier. C'est pourquoi je l'ai appelé "eager", car l'utilisateur n'a que pseudo-sélectionné l'option, comme un pointage ou un focus, mais n'a pas confirmé le choix avec enter ou click. L'événement "eager" rend cette fonctionnalité de modification de catégorie de composant inaccessible, car ouvrir la case de sélection et parcourir un élément déclenchera l'événement et modifiera la page avant que l'utilisateur ne soit prêt.

Amélioration de l'événement "<select>" modifié

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 pression du clavier sur chaque élément <select> et à déterminer si l'appui sur la touche correspond à une confirmation de navigation (Tab ou Enter) ou à la navigation spatiale (ArrowUp ou ArrowDown). Ainsi, le composant peut décider d'attendre ou de quitter la page, lorsque l'événement de l'élément <select> se déclenche.

Conclusion

Maintenant que vous savez comment j'ai fait, comment feriez-vous ? 😃

Diversifiez nos approches et découvrons toutes les manières de créer des applications sur le Web. Créez une démo, envoyez-moi des tweets via des liens et je l'ajouterai à la section "Remix de la communauté" ci-dessous.

Remix de la communauté