Atelier de programmation: Créer un composant Sidenav

Cet atelier de programmation vous explique comment créer un composant de mise en page responsif de navigation latérale avec glissement vers l'extérieur sur le Web. Nous allons créer le composant au fur et à mesure, en commençant par HTML, CSS, puis JavaScript.

Consultez mon article de blog Créer un composant Sidenav pour en savoir plus sur les fonctionnalités de la plate-forme Web CSS choisies pour créer ce composant.

Préparation

  1. Cliquez sur Remix to Edit (Remixer pour modifier) pour rendre le projet modifiable.
  2. Ouvrez app/index.html.

HTML

Tout d'abord, obtenez les bases de la configuration HTML afin d'y avoir du contenu et quelques zones de travail.

Déposez le code HTML suivant dans la balise <body>.

<aside></aside>
<main></main>

<aside> contient le menu de navigation en tant qu'élément complémentaire de <main>, qui contient le contenu principal de la page.

Nous allons ensuite remplir ces éléments sémantiques avec le reste du contenu de la page.

Ajoutez un élément de navigation, des liens de navigation et un lien de fermeture dans l'élément <aside>.

<aside>
  <nav>
    <h4>My</h4>
    <a href="#">Dashboard</a>
    <a href="#">Profile</a>
    <a href="#">Preferences</a>
    <a href="#">Archive</a>

    <h4>Settings</h4>
    <a href="#">Accessibility</a>
    <a href="#">Theme</a>
    <a href="#">Admin</a>
  </nav>

  <a href="#"></a>
</aside>

Les liens fonctionnent parfaitement dans les éléments <nav> et les éléments <nav> dans les barres latérales <aside>. Toutefois, nous pouvons encore nous améliorer.

Dans l'élément de contenu principal, ajoutez un en-tête et un article pour contenir sémantiquement le contenu de la mise en page.

<main>
  <header>
    <a href="#sidenav-open" class="hamburger">
      <svg viewBox="0 0 50 40">
        <line x1="0" x2="100%" y1="10%" y2="10%" />
        <line x1="0" x2="100%" y1="50%" y2="50%" />
        <line x1="0" x2="100%" y1="90%" y2="90%" />
      </svg>
    </a>
    <h1>Site Title</h1>
  </header>

  <article>
    {put some placeholder content here}
  </article>
</main>

L'en-tête contient le lien d'ouverture du menu. L'a côté a le bouton de fermeture. Nous afficherons et masquerons bientôt des éléments en fonction de la taille de la fenêtre d'affichage.

Dans l'élément <article>, nous avons collé une phrase d'espace réservé. Remplacez `` par le vôtre ou collez le lorem fourni ci-dessous:

<h2>Totam Header</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Cum consectetur, necessitatibus velit officia ut impedit veritatis temporibus soluta? Totam odit cupiditate facilis nisi sunt hic necessitatibus voluptatem nihil doloribus! Enim.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead Totam Odit</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

<h3>Subhead</h3>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Fugit rerum, amet odio explicabo voluptas eos cum libero, ex esse quasi optio incidunt soluta eligendi labore error corrupti! Dolore, cupiditate porro.</p>

Ce contenu et sa longueur permettent de faire défiler la page lorsqu'elle dépasse la hauteur de la fenêtre d'affichage.

Jusqu'à présent, vous avez ajouté un élément de côté, avec une barre de navigation, des liens et un moyen de fermer la barre de navigation latérale. Vous avez également ajouté un en-tête, un moyen d'ouvrir le panneau de navigation latéral et un article à l'élément principal. C'est déjà clair, sémantique et assez intemporel, mais nous pouvons le rendre plus clair et plus clair pour tout le monde. Le lien ouvert dans le menu latéral pourrait être plus clairement marqué.

Ajoutez les attributs title et aria-label à l'élément "open link" d'en-tête:

<a href="#sidenav-open" class="hamburger">
<a href="#sidenav-open" title="Open Menu" aria-label="Open Menu" class="hamburger">

L'icône SVG ouverte pourrait également être plus clairement indiquée. Ajoutez les attributs suivants au SVG dans l'élément "Open Link" :

<svg viewBox="0 0 50 40">
<svg viewBox="0 0 50 40" role="presentation" focusable="false" aria-label="trigram for heaven symbol">

Le lien de fermeture dans le panneau de navigation latéral pourrait être plus clairement indiqué. Ajoutez les attributs title et aria-label à l'élément de lien de fermeture de navigation latérale:

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

CSS

Il est temps de mettre en page les éléments. Le contenu principal et la barre de navigation latérale sont des enfants directs de la balise <body>. Il s'agit donc d'un bon point de départ.

Ajoutez le code CSS suivant dans css/sidenav.css afin que l'élément <body> dispose les éléments enfants.

body {
  display: grid;
  grid: [stack] 1fr / min-content [stack] 1fr;

  @media (max-width: 540px) {
    & > :matches(aside, main) {
      grid-area: stack;
    }
  }
}

Cette mise en page indique essentiellement: créez une ligne nommée stack contenant tout le contenu et deux colonnes de cette ligne, dont la deuxième est également nommée stack. La première colonne doit être dimensionnée en fonction de ses besoins minimaux en contenu, et la deuxième colonne peut occuper le reste. Ensuite, dans une fenêtre d'affichage contrainte de 540px ou moins, placez les éléments de contenu principal et de navigation latérale sur la même ligne et la même colonne, de sorte qu'ils se superposent dans une grille de 1 x 1.

Sur la base de cette fonctionnalité d'empilement responsif, nous pouvons désormais exploiter l'état de la barre d'adresse pour activer/désactiver le style de visibilité et de transition de la barre de navigation latérale.

Remettez à jour l'élément <aside> dans app/index.html:

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

Cela permet au code CSS de faire correspondre un élément et le hachage de l'URL. C'est important pour l'utilisation de :target. L'ID de l'élément peut maintenant correspondre au hachage d'URL que nous allons définir avec les balises <a>.

En outre, pour faciliter le ciblage JavaScript, ajoutez des ID pour les éléments clés qui contrôlent le menu de navigation latéral. Tout d'abord, ajoutez un ID au lien d'ouverture de la barre de navigation latérale:

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

Ajoutez ensuite un ID au lien de fermeture de la barre de navigation latérale:

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

Cela conclut la mise en page de la pile responsive <body> pour la macro et nous relie à la barre d'adresse. Continuons.

<aside> présente également une mise en page soignée. Elle a deux enfants, un <nav> (le composant d'apparence papier qui glisse) et un élément de lien <a> fermant qui définit l'URL sur #. Le lien est invisible à droite de la barre de navigation papier. Il permet aux utilisateurs de cliquer sur le composant visuel pour le fermer.

Ajoutez le CSS suivant à css/sidenav.css:

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

Je pensais que le ratio et les noms étaient très agréables ici, où la grille pouvait briller et donner au concepteur beaucoup de contrôle.

Ensuite, je dois superposer de manière conditionnelle le contenu principal et conserver ma position dans n'importe quel défilement du document. C'est un excellent travail pour position: sticky et certaines overscroll-behavior.

Ajoutez les styles suivants pour le panneau de navigation latéral:

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

  @media (max-width: 540px) {
    position: sticky;
    top: 0;
    max-height: 100vh;
    overflow: hidden auto;
    overscroll-behavior: contain;

    visibility: hidden; /* not keyboard accessible when closed */
  }
}

Ces styles garantissent que la barre de navigation latérale correspond à la hauteur de la fenêtre d'affichage, peut faire défiler l'écran verticalement et contient le défilement. Plus important encore, elle masque l'élément. Par défaut, lorsque la fenêtre d'affichage est 540px ou plus petite, masquez cette barre de navigation latérale. À moins !

Ajoutez un pseudo-sélecteur :target à l'élément #sidenav-open:

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

Lorsque l'ID de cet élément et la barre d'URL sont identiques, définissez visibility sur visible. Ouvrez le menu latéral après avoir fait défiler la page ou essayez de la faire défiler lorsque le panneau de navigation latéral est ouvert. Qu'en pensez-vous ?

Ajoutez le code CSS suivant en bas de app/sidenav.css:

#sidenav-button,
#sidenav-close {
  -webkit-tap-highlight-color: transparent;
  -webkit-touch-callout: none;
  user-select: none;
  touch-action: manipulation;

  @media (min-width: 540px) {
    display: none;
  }
}

Ces styles ciblent les boutons d'ouverture et de fermeture, spécifient leurs styles d'appui et d'appui, et les masquent également lorsque les fenêtres d'affichage sont supérieures ou égales à 540px.

Pour plus d'élégance, ajoutons des transformations CSS en respectant l'accessibilité. Ajoutez le CSS suivant à css/sidenav.css:

#sidenav-open {
  --easeOutExpo: cubic-bezier(0.16, 1, 0.3, 1);
  --duration: .6s;

  ...

  @media (max-width: 540px) {
    ...

    transform: translateX(-110vw);
    will-change: transform;
    transition:
      transform var(--duration) var(--easeOutExpo),
      visibility 0s linear var(--duration);

    &:target {
      visibility: visible;
      transform: translateX(0);
      transition: transform var(--duration) var(--easeOutExpo);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    --duration: 1ms;
  }
}
Démonstration de l'interaction avec et sans durée appliquée en fonction de la requête média "prefers-reduced-motion".

Sprinkle dans du code JavaScript

La touche Escape doit fermer le menu. Ajoutez ce JS à js/index.js:

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

sidenav.addEventListener('keyup', e => {
  if (e.code === 'Escape') {
    document.location.hash = '';
  }
});

Écoute un événement de touche sur l'élément de navigation latérale. Si la valeur est Escape, le hachage de l'URL est vide, ce qui facilite la transition de la barre de navigation latérale.

Le prochain élément de JS UX est la gestion des sélections. Je veux faciliter l'ouverture et la fermeture. J'attends donc que la barre de navigation latérale ait terminé une transition, puis effectue une vérification croisée avec le hachage de l'URL pour déterminer si elle est à l'intérieur ou à l'extérieur. J'utilise ensuite JavaScript pour mettre le bouton en évidence sur celui sur lequel il vient d'appuyer.

Ajoutez le code JavaScript suivant à js/index.js:

const closenav = document.querySelector('#sidenav-close');
const opennav = document.querySelector('#sidenav-button');

sidenav.addEventListener('transitionend', e => {
  if (e.propertyName !== 'transform') {
    return;
  }

  const isOpen = document.location.hash === '#sidenav-open';

  isOpen
    ? closenav.focus()
    : opennav.focus();
});

Essayer

  • Pour prévisualiser le site, appuyez sur Afficher l'application, puis sur Plein écran plein écran.

Conclusion

C'est un résumé des besoins que j'avais avec le composant. N'hésitez pas à vous appuyer dessus, à le piloter avec un état JavaScript au lieu de l'URL et, en général, à vous l'approprier. Il y a toujours plus à ajouter ou de cas d'utilisation à couvrir.

Ouvrez css/brandnav.css pour consulter les styles qui ne sont pas liés à la mise en page que j'ai appliqués à ce composant. Je ne pensais pas qu'il était important pour l'ensemble de fonctionnalités sur lequel je me concentrais et j'espérais que séparer les styles de la mise en page encouragerait le copier-coller. Vous pourriez en apprendre plus !

Comment faire glisser des composants de navigation latérale responsive ? Vous arrive-t-il d'en avoir plusieurs, par exemple des deux côtés ? J'aimerais présenter votre solution dans une vidéo YouTube. N'hésitez pas à m'envoyer un tweet ou à laisser un commentaire sur YouTube en incluant votre code, cela aidera tout le monde !