Créer un composant de navigation latérale

Présentation générale de la création d'un panneau de navigation latéral glissant et responsif

Dans ce post, je veux vous montrer comment j'ai conçu un prototype d'un composant Sidenav pour le Web qui est réactif, avec état, prend en charge la navigation au clavier, fonctionne avec et sans JavaScript, et fonctionne sur tous les navigateurs. Essayez la démonstration.

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

Présentation

Créer un système de navigation réactif est difficile. Certains utilisateurs seront sur un clavier, Certains disposeront d'un ordinateur de bureau puissant, et d'autres y accéderont à partir d'un petit appareil mobile. Tous les visiteurs devraient pouvoir ouvrir et fermer le menu.

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder"> <ph type="x-smartling-placeholder">
</ph> Démonstration de la mise en page responsive sur ordinateur à mobile
. <ph type="x-smartling-placeholder"> <ph type="x-smartling-placeholder">
</ph> Thèmes clair et sombre non disponibles sur iOS et Android

Tactiques Web

Pour cette exploration des composants, j'ai eu le plaisir de combiner quelques fonctionnalités essentielles de la plate-forme Web:

  1. CSS :target
  2. Grille CSS
  3. Transformations CSS
  4. Requêtes média CSS pour la fenêtre d'affichage et les préférences de l'utilisateur
  5. JS pour focus améliorations de l'expérience utilisateur

Ma solution comporte une barre latérale et ne s'active que sur un appareil mobile La fenêtre d'affichage ne doit pas dépasser 540px. 540px sera notre point d'arrêt pour basculer entre la mise en page interactive sur mobile et la mise en page statique pour ordinateur de bureau.

Pseudo-classe CSS :target

Un lien <a> définit le hachage de l'URL sur #sidenav-open et l'autre sur vide (''). Enfin, un élément possède le id correspondant au hachage:

<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<aside id="sidenav-open">
  …
</aside>

En cliquant sur chacun de ces liens, vous modifiez l'état de hachage de l'URL de votre page, puis avec une pseudo-classe, j'affiche et je masque la navigation latérale:

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
  }

  #sidenav-open:target {
    visibility: visible;
  }
}

Grille CSS

Auparavant, je n'utilisais que la position absolue ou fixe les mises en page et les composants de Sidenav. Cependant, la grille, avec sa syntaxe grid-area, permet d'affecter plusieurs éléments à la même ligne ou colonne.

Piles

L'élément de mise en page principal #sidenav-container est une grille qui crée une ligne et deux colonnes. L'une d'elles est nommée stack. Lorsque l'espace est limité, le CSS attribue tous les attributs de l'élément <main> enfants au même nom de grille, en plaçant tous les éléments dans le même espace, ce qui crée une pile.

#sidenav-container {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;
  min-height: 100vh;
}

@media (max-width: 540px) {
  #sidenav-container > * {
    grid-area: stack;
  }
}

<aside> est l'élément d'animation qui contient la barre de navigation latérale. Il contient Deux enfants: le conteneur de navigation <nav> nommé [nav] et un arrière-plan <a> nommé [escape], qui permet de fermer le menu.

#sidenav-open {
  display: grid;
  grid-template-columns: [nav] 2fr [escape] 1fr;
}

Ajuster 2fr et 1fr pour trouver le format souhaité pour la superposition de menu et son bouton de fermeture d'espace négatif.

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder"> <ph type="x-smartling-placeholder">
</ph> Une démonstration illustrant ce qui se passe lorsque vous modifiez le format.

Transformations 3D CSS et transitions

Notre mise en page est désormais empilée dans une fenêtre d'affichage mobile. En attendant d'y ajouter de nouveaux styles, elle se superpose à notre article par défaut. Voici quelques exemples UX que je vise dans la section suivante:

  • Animer l'ouverture et la fermeture
  • N'animez l'animation avec des mouvements que si l'utilisateur est d'accord.
  • Animer visibility pour que le curseur ne passe pas dans l'élément hors écran

Lorsque je commence à implémenter des animations de mouvement, je veux commencer par l’accessibilité en tête.

Mouvement accessible

Tout le monde ne voudra pas une expérience de mouvement coulissante. Dans notre solution, cette préférence est appliqué en ajustant une variable CSS --duration dans une requête média. Cette valeur de requête média représente les préférences d'un utilisateur pour le système d'exploitation pour les mouvements (le cas échéant) ;

#sidenav-open {
  --duration: .6s;
}

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
. Une démonstration de l'interaction avec et sans durée appliquée.

Maintenant, lorsque notre navigation latérale s'ouvre et se ferme, si un utilisateur préfère réduire les mouvements, Je déplace instantanément l'élément dans la vue, en conservant son état sans mouvement.

Transition, transformation, traduction

Sortie du panneau latéral (par défaut)

Pour définir l'état par défaut de notre navigation side sur mobile sur un état hors écran, Je positionne l'élément avec transform: translateX(-110vw).

Notez que j'ai ajouté un autre 10vw au code hors écran habituel de -100vw, pour s'assurer que le box-shadow du panneau de navigation latéral n'apparaît pas dans la fenêtre d'affichage principale lorsqu'elle est masquée.

@media (max-width: 540px) {
  #sidenav-open {
    visibility: hidden;
    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);
  }
}
Navigation latérale dans

Lorsque l'élément #sidenav correspond à :target, définissez la position translateX() sur la base d'origine 0. et observez le fait que le CSS fait glisser l'élément de sa position -110vw vers l'entrée la position de 0 sur var(--duration) lorsque le hachage de l'URL est modifié.

@media (max-width: 540px) {
  #sidenav-open:target {
    visibility: visible;
    transform: translateX(0);
    transition:
      transform var(--duration) var(--easeOutExpo);
  }
}

Visibilité de la transition

L'objectif est maintenant de masquer le menu des lecteurs d'écran lorsqu'il est terminé, afin que les systèmes ne mettent pas le focus dans un menu hors écran. Pour ce faire, je définis transition de visibilité lorsque :target change.

  • Lors de l'ouverture, ne changez pas la visibilité. être visible immédiatement afin que je puisse voir l'élément glisser vers l'intérieur et accepter le focus.
  • Lors de la sortie, la visibilité de la transition est retardée, de sorte qu'elle bascule sur hidden à la fin de la transition.

Améliorations apportées à l'accessibilité de l'expérience utilisateur

Cette solution repose sur la modification de l'URL afin que l'état soit géré. Naturellement, l'élément <a> doit être utilisé ici pour obtenir une bonne accessibilité. de Google Workspace. Ajoutons à nos éléments interactifs des étiquettes qui expriment clairement l'intention.

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>

<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
  <svg>...</svg>
</a>
<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
. Démonstration de l'expérience utilisateur avec la voix off et l'interaction avec le clavier

Désormais, nos principaux boutons d'interaction indiquent clairement leur utilité pour la souris et pour le clavier.

:is(:hover, :focus)

Ce pseudo-sélecteur fonctionnel CSS nous permet d'être rapidement inclusifs avec nos styles de survol en les partageant également avec le curseur.

.hamburger:is(:hover, :focus) svg > line {
  stroke: hsl(var(--brandHSL));
}

Sprinkle sur JavaScript

Appuyez sur escape pour fermer

La touche Escape de votre clavier devrait fermer le menu, n'est-ce pas ? Raccordons-nous au câble.

const sidenav = document.querySelector('#sidenav-open');

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
Historique du navigateur

Afin d'éviter que l'interaction ouverte et fermée n'empile plusieurs dans l'historique du navigateur, ajoutez le code JavaScript suivant Bouton de fermeture:

<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>

L'entrée de l'historique de l'URL sera supprimée lors de sa fermeture, faisant ainsi croire que le menu était jamais ouvert.

Cibler l'expérience utilisateur

L'extrait suivant nous aide à mettre l'accent sur les boutons d'ouverture et de fermeture après qu'ils ouvrent ou ferment. Je veux faciliter l'activation ou la désactivation.

sidenav.addEventListener('transitionend', e => {
  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
      ? document.querySelector('#sidenav-close').focus()
      : document.querySelector('#sidenav-button').focus();
})

Lorsque le panneau de navigation latéral s'ouvre, sélectionnez le bouton de fermeture. Lorsque le panneau de navigation latéral se ferme, sélectionner le bouton d'ouverture. Pour ce faire, j'appelle focus() sur l'élément dans JavaScript.

Conclusion

Maintenant que tu sais comment j'ai fait, comment faire ?! Cela permet d'obtenir une architecture de composants amusante. Qui va créer la première version avec des emplacements ? 🙂

Diversifions et à découvrir toutes les méthodes de développement sur le Web. créer un Glitch ; me envoyer un tweet pour obtenir votre version, et je l'ajouterai au section Remix de la communauté ci-dessous.

Remix de la communauté