Aperçu de base sur la façon de créer un composant de fil d'Ariane responsif et accessible pour permettre aux utilisateurs de naviguer sur votre site.
Dans cet article, je vais vous expliquer comment créer des composants de fil d'Ariane. Tester la fonctionnalité
Si vous préférez les vidéos, voici une version YouTube de cet article :
Présentation
Un composant fil d'Ariane indique à l'utilisateur où il se trouve dans la hiérarchie du site. Le nom est tiré de Hansel et Gretel, qui ont semé des miettes de pain derrière eux dans une forêt sombre et ont pu retrouver leur chemin en les suivant à l'envers.
Les breadcrumbs de cet article ne sont pas des breadcrumbs standards, mais plutôt des éléments qui y ressemblent. Ils offrent des fonctionnalités supplémentaires en plaçant les 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 des composants ci-dessus, les catégories de l'espace réservé sont des genres de jeux vidéo. Ce parcours est créé en suivant le chemin d'accès suivant : home »
rpg » indie » on sale, comme indiqué ci-dessous.
Ce composant de fil d'Ariane doit permettre aux utilisateurs de parcourir cette hiérarchie d'informations, de passer d'une branche à l'autre et de sélectionner des pages rapidement et précisément.
Architecture de l'information
Je trouve qu'il est utile de penser en termes de collections et d'éléments.
Collections
Une collection est un tableau d'options au choix. Sur la page d'accueil du prototype de fil d'Ariane de ce post, les collections sont FPS, RPG, brawler, dungeon crawler, sports 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 la collection. Par exemple, ils 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, Indie et Auto-édité.
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 auront une architecture de l'information (AI) personnalisée qui créera un tableau multidimensionnel différent. Toutefois, j'espère que le concept de pages de destination de collection et de parcours hiérarchique pourra également être intégré à votre fil d'Ariane.
Mises en page
Annoter
De bons composants commencent par un code HTML approprié. Dans la section suivante, je vais aborder mes choix 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 indique au navigateur que cette page souhaite utiliser les styles clairs et sombres du navigateur. Les exemples de fil d'Ariane n'incluent aucun code CSS pour ces schémas de couleurs. Ils utiliseront donc les couleurs par défaut fournies par le navigateur.
Élément de navigation
<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 ARIA implicite de navigation.
Lors des tests, j'ai remarqué que l'attribut role modifiait la façon dont un lecteur d'écran interagissait avec l'élément. En effet, il était annoncé comme une 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 évite de répéter les mêmes informations sur le chemin d'accès, ce qui entraînerait des documents plus volumineux et un risque d'incohérence du chemin d'accès.
Pour utiliser cette technique, ajoutez un élément SVG masqué à la page et insérez 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 continue avec le reste de la page en référençant l'ID pour d'autres utilisations 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>

Définissez-les une seule fois et utilisez-les 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 aux utilisateurs qui ne font qu'écouter le contenu. En les masquant, vous évitez d'ajouter du bruit inutile.
Lien divisé .crumb
C'est là que le fil d'Ariane traditionnel et celui de ce composant divergent.
Normalement, il ne s'agirait que d'un lien <a>, mais j'ai ajouté une UX de traversée avec une sélection masquée. La classe .crumb est responsable de la mise en page du lien et de l'icône, tandis que .crumbicon est responsable de l'empilement de l'icône et de l'élément de sélection. Je l'ai appelé lien fractionné, car ses fonctions sont très semblables à celles d'un bouton fractionné, 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 quelques options ne sont pas exceptionnels, mais ajoutent plus de 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. Toutefois, il fournit la même aide à tous les autres utilisateurs. Vous le verrez au premier plan sur l'iPad. Un attribut fournit le contexte du bouton à de nombreux utilisateurs.

Décorations de séparateur
<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). J'attribue ensuite à chaque aria-hidden="true", car il s'agit d'éléments décoratifs et non d'éléments que le lecteur d'écran doit annoncer.
La propriété gap, que nous allons aborder ensuite, permet d'espacer ces éléments de manière simple.
Styles
Étant donné que la couleur utilise les couleurs système, il s'agit principalement d'espaces et de piles pour les styles.
Orientation et flux de la mise en page

L'élément de navigation principal nav.breadcrumbs définit une propriété personnalisée à portée limitée que les enfants peuvent utiliser. Il établit également une mise en page horizontale alignée verticalement. Cela permet de s'assurer que les crumbs, 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);
}

Chaque .crumb établit également une mise en page horizontale alignée verticalement avec un certain espace, mais cible spécifiquement ses enfants de lien et spécifie le style white-space: nowrap. C'est essentiel pour les fil d'Ariane comportant plusieurs 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 distingue du reste. Les utilisateurs de lecteurs d'écran disposent ainsi d'un indicateur clair leur permettant de savoir que le lien renvoie vers la page actuelle. Nous avons également stylisé l'élément visuellement pour offrir une expérience utilisateur similaire aux utilisateurs voyants.
Le composant .crumbicon utilise une grille pour empiler une icône SVG avec un élément <select> "presque invisible".

.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 dans le DOM. Il se trouve donc en haut de la pile et est interactif. Ajoutez un style de opacity: .01 pour que l'élément reste utilisable. Le résultat est 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
Les breadcrumbs doivent pouvoir représenter un très long chemin. J'aime bien laisser les éléments sortir de l'écran horizontalement, quand c'est approprié, et j'ai trouvé que ce composant de fil d'Ariane s'y prêtait 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épassement configurent l'UX suivante :
- Défilement horizontal avec confinement du défilement hors limites.
- Marge intérieure de défilement horizontal.
- Un point d'accroche sur la dernière miette. Cela signifie qu'au chargement de la page, le premier élément du fil d'Ariane se charge de manière alignée et visible.
- Supprime le point d'accrochage de Safari, qui a du mal avec les combinaisons de défilement horizontal et d'effet d'accrochage.
Requêtes média
Pour les petits Viewports, un ajustement subtil consiste à masquer le libellé "Accueil" et à ne laisser que l'icône :
@media (width <= 480px) {
.breadcrumbs .home-label {
display: none;
}
}

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 autre style n'a besoin d'être modifié. Les effets de survol et de sélection sont excellents et pertinents sans transition, mais si le mouvement est autorisé, 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 la page appropriée doit être affichée. Deuxièmement, pour normaliser l'expérience utilisateur, assurez-vous qu'aucune navigation inattendue ne se produit lorsque les utilisateurs parcourent simplement les options <select>.
Deux mesures essentielles de l'expérience utilisateur doivent être gérées par JavaScript : la prévention du déclenchement de l'événement de modification de la sélection et de l'événement de modification <select>.
La prévention des événements anticipés est nécessaire en raison de l'utilisation d'un élément <select>. Sur Windows Edge, et probablement sur d'autres navigateurs également, l'événement de sélection changed se déclenche lorsque l'utilisateur parcourt les options au clavier. C'est pourquoi je l'ai appelé "eager" (impatient), car l'utilisateur n'a fait que pseudo-sélectionner l'option, comme un survol ou une mise au point, mais n'a pas confirmé son choix avec enter ou click. L'événement eager rend cette fonctionnalité de modification de la catégorie de composant inaccessible, car l'ouverture de la zone de sélection et la simple navigation dans 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 <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 touche enfoncée sur chaque élément <select> et à déterminer si la touche enfoncée était une touche de confirmation de navigation (Tab ou Enter) ou de navigation spatiale (ArrowUp ou ArrowDown). Grâce à cette détermination, le composant peut décider d'attendre ou de passer à l'étape suivante lorsque l'événement de l'élément <select> se déclenche.
Conclusion
Maintenant que vous savez comment j'ai fait, comment feriez-vous ? 🙂
Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez une démo, tweetez-moi les liens et je l'ajouterai à la section des remix de la communauté ci-dessous !
Remix de la communauté
- Tux Solbakk en tant que composant Web : démo et code