Présentation de base sur la création d'un sidenav coulissant et responsif
Dans cet article, je vais vous expliquer comment j'ai prototypé un composant Sidenav pour le Web, qui est réactif, avec état, compatible avec la navigation au clavier, fonctionnant avec ou sans JavaScript, et fonctionnant sur tous les navigateurs. Essayez la démonstration.
Si vous préférez les vidéos, voici une version YouTube de cet article :
Présentation
Il est difficile de créer un système de navigation responsif. Certains utilisateurs utiliseront un clavier, d'autres un ordinateur puissant et d'autres encore un petit appareil mobile. Tous les visiteurs doivent pouvoir ouvrir et fermer le menu.
Tactiques Web
Dans cette exploration de composants, j'ai eu le plaisir de combiner quelques fonctionnalités essentielles de la plate-forme Web :
- CSS
:target
- Grille CSS
- Transformations CSS
- Requêtes média CSS pour la fenêtre d'affichage et les préférences utilisateur
- JS pour
focus
Améliorations de l'UX
Ma solution comporte une barre latérale qui bascule uniquement lorsque la fenêtre d'affichage "mobile" est de 540px
ou moins.
540px
sera notre point d'arrêt pour basculer entre la mise en page interactive pour mobile et la mise en page statique pour ordinateur.
Pseudo-classe CSS :target
Un lien <a>
définit le hachage de l'URL sur #sidenav-open
et l'autre sur une valeur vide (''
). Enfin, un élément possède le id
pour correspondre 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, l'état du hachage de l'URL de notre page change. Ensuite, avec une pseudo-classe, j'affiche et masque la barre de navigation latérale :
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
Grille CSS
Auparavant, je n'utilisais que des mises en page et des composants de navigation latérale à position absolue ou fixe. La grille, avec sa syntaxe grid-area
, nous permet d'attribuer 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, dont une de chaque est nommée stack
. Lorsque l'espace est limité, le CSS attribue le même nom de grille à tous les enfants de l'élément <main>
, 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;
}
}
Arrière-plan du menu
<aside>
est l'élément d'animation qui contient la navigation latérale. Il comporte 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;
}
Ajustez 2fr
et 1fr
pour trouver le ratio qui vous convient pour la superposition du menu et son bouton de fermeture de l'espace négatif.
Transformations et transitions 3D CSS
Notre mise en page est maintenant empilée à la taille d'une fenêtre d'affichage mobile. Tant que je n'ajoute pas de nouveaux styles, il recouvre notre article par défaut. Voici quelques éléments d'UX que je vise dans la section suivante :
- Animer l'ouverture et la fermeture
- N'animer avec du mouvement que si l'utilisateur est d'accord
- Animer
visibility
pour que la sélection au clavier n'atteigne pas l'élément hors écran
Lorsque je commence à implémenter des animations de mouvement, je veux d'abord penser à l'accessibilité.
Mouvement accessible
Tout le monde ne souhaitera pas bénéficier d'une expérience de mouvement de type "slide out". Dans notre solution, cette préférence est appliquée en ajustant une variable CSS --duration
à l'intérieur d'une requête média. Cette valeur de requête média représente la préférence de l'utilisateur concernant le mouvement dans son système d'exploitation (si disponible).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Désormais, lorsque notre sidenav s'ouvre et se ferme, si un utilisateur préfère les mouvements réduits, je déplace instantanément l'élément dans la vue, en conservant l'état sans mouvement.
Transition, transformation, traduction
Sidenav out (par défaut)
Pour définir l'état par défaut de notre sidenav 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 typique de -100vw
, pour m'assurer que le box-shadow
de la barre de navigation latérale ne s'affiche pas dans la fenêtre d'affichage principale lorsqu'il est masqué.
@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);
}
}
Barre latérale dans
Lorsque l'élément #sidenav
correspond à :target
, définissez la position translateX()
sur la position de base 0
. Ensuite, regardez le code CSS faire glisser l'élément de sa position de sortie -110vw
à sa position d'entrée 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 désactivé, afin que les systèmes ne mettent pas l'accent sur un menu hors écran. Pour ce faire, je définis une transition de visibilité lorsque :target
change.
- Lorsque vous entrez, ne faites pas de transition de visibilité ; soyez visible immédiatement pour que je puisse voir l'élément glisser et accepter la mise au point.
- Lorsque vous sortez, la visibilité de la transition est retardée, de sorte qu'elle passe à
hidden
à la fin de la transition de sortie.
Améliorations de l'expérience utilisateur en termes d'accessibilité
Liens
Cette solution repose sur la modification de l'URL pour que l'état soit géré.
L'élément <a>
doit naturellement être utilisé ici, et il bénéficie sans frais de fonctionnalités d'accessibilité intéressantes. Ajoutons des libellés à nos éléments interactifs pour indiquer clairement leur 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>
Désormais, nos boutons d'interaction principaux indiquent clairement leur intention pour la souris et le clavier.
:is(:hover, :focus)
Ce pseudo-sélecteur fonctionnel CSS pratique nous permet d'être rapidement inclusifs avec nos styles de survol en les partageant également avec la mise au point.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Ajouter Sprinkle à JavaScript
Appuyez sur escape
pour fermer
La touche Escape
de votre clavier devrait fermer le menu, n'est-ce pas ? Connectons-le.
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', event => {
if (event.code === 'Escape') document.location.hash = '';
});
Historique du navigateur
Pour éviter que l'interaction d'ouverture et de fermeture n'empile plusieurs entrées dans l'historique du navigateur, ajoutez le code JavaScript suivant en ligne au bouton de fermeture :
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>
Cela supprimera l'entrée de l'historique des URL à la fermeture, comme si le menu n'avait jamais été ouvert.
Focus UX
L'extrait suivant nous aide à mettre l'accent sur les boutons d'ouverture et de fermeture après leur ouverture ou leur fermeture. Je veux pouvoir activer/désactiver facilement.
sidenav.addEventListener('transitionend', e => {
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? document.querySelector('#sidenav-close').focus()
: document.querySelector('#sidenav-button').focus();
})
Lorsque la barre de navigation latérale s'ouvre, le bouton de fermeture est sélectionné. Lorsque la barre de navigation latérale se ferme, le bouton d'ouverture est sélectionné. Pour ce faire, j'appelle focus()
sur l'élément en JavaScript.
Conclusion
Maintenant que vous savez comment j'ai fait, comment feriez-vous ? L'architecture des composants est donc très amusante ! Qui va créer la première version avec des emplacements ? 🙂
Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez un remix sur Glitch, tweetez-le moi et je l'ajouterai à la section Remix de la communauté ci-dessous.
Remix de la communauté
- @_developit avec des éléments personnalisés : démo et code
- @mayeedwin1 avec HTML/CSS/JS : démo et code
- @a_nurella avec un remix Glitch : démonstration et code
- @EvroMalarkey avec HTML/CSS/JS : démo et code