Creazione di un componente di navigazione laterale

Una panoramica di base su come creare una navigazione laterale reattiva con slide

In questo post voglio condividere con voi come ho prototipato un componente Sidenav per il web che è reattivo, stateful, supporta la navigazione da tastiera, funziona con e senza JavaScript e funziona su tutti i browser. Prova la demo.

Se preferisci i video, ecco una versione di questo post su YouTube:

Panoramica

È difficile creare un sistema di navigazione reattivo. Alcuni utenti useranno una tastiera, altri avranno computer potenti e altri ancora accederanno da un piccolo dispositivo mobile. Tutte le persone che visitano il sito dovrebbero essere in grado di aprire e chiudere il menu.

Demo del layout adattabile da desktop a mobile
Tema chiaro e scuro disponibile su iOS e Android

Tattiche sul web

In questa esplorazione dei componenti ho avuto la gioia di combinare alcune funzionalità fondamentali della piattaforma web:

  1. Servizio di shopping comparativo :target
  2. Griglia CSS
  3. CSS transforms
  4. Query multimediali CSS per area visibile e preferenza dell'utente
  5. JS per focus miglioramenti dell'esperienza utente

La mia soluzione ha una barra laterale e si attiva solo quando in un'area visibile "dispositivi mobili" pari o inferiore a 540px. 540px sarà il nostro punto di interruzione per passare dal layout interattivo mobile a quello statico per computer e viceversa.

Pseudo-classe :target CSS

Un link <a> imposta l'hash dell'URL su #sidenav-open e l'altro su vuoto (''). Infine, un elemento ha id per corrispondere all'hash:

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

Facendo clic su ognuno di questi link viene modificato lo stato hash dell'URL della pagina, quindi con una pseudo-classe I mostri e nascondo la barra di navigazione laterale:

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

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

Griglia CSS

In passato utilizzavo solo layout e componenti di navigazione laterale con posizione assoluta o fissa. La griglia, però, grazie alla sintassi grid-area, consente di assegnare più elementi alla stessa riga o colonna.

Impilati

L'elemento di layout principale #sidenav-container è una griglia che crea 1 riga e 2 colonne, una di ognuna è denominata stack. Quando lo spazio è vincolato, CSS assegna tutti gli elementi secondari dell'elemento <main> allo stesso nome della griglia, inserendo tutti gli elementi nello stesso spazio, creando una pila.

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

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

<aside> è l'elemento animato che contiene la barra di navigazione laterale. Ha 2 elementi secondari: il contenitore di navigazione <nav> denominato [nav] e uno sfondo <a> denominato [escape], che viene utilizzato per chiudere il menu.

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

Modifica 2fr e 1fr per trovare le proporzioni che preferisci per l'overlay del menu e il relativo pulsante di chiusura dello spazio negativo.

Una demo di cosa succede quando modifichi il rapporto.

Trasformazioni e transizioni 3D CSS

Il nostro layout è ora impilato con dimensioni dell'area visibile su dispositivi mobili. Finché non aggiungo nuovi stili, si sovrappone al nostro articolo per impostazione predefinita. Ecco alcune UX che cercherò nella prossima sezione:

  • Anima apertura e chiusura
  • L'animazione con il movimento viene eseguita solo se l'utente è d'accordo.
  • Attiva l'animazione visibility in modo che lo stato attivo della tastiera non entri nell'elemento fuori schermo

Man mano che inizio a implementare le animazioni di movimento, voglio partire con la massima priorità sull'accessibilità.

Movimenti accessibili

Non a tutti vorranno provare l'esperienza di movimento. Nella nostra soluzione questa preferenza viene applicata modificando una variabile CSS --duration all'interno di una query multimediale. Questo valore di query supporti rappresenta la preferenza di un utente sul sistema operativo per il movimento (se disponibile).

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

@media (prefers-reduced-motion: reduce) {
  #sidenav-open {
    --duration: 1ms;
  }
}
Una demo dell'interazione con e senza durata applicata.

Ora, quando la barra di navigazione laterale scorre aperta e chiusa, se un utente preferisce movimento ridotto, sposto immediatamente l'elemento nella vista, mantenendo lo stato senza movimento.

Transizione, trasformazione e traslazione

Navigazione laterale in uscita (predefinita)

Per impostare lo stato predefinito della navigazione laterale sui dispositivi mobili su uno stato fuori schermo, posiziono l'elemento con transform: translateX(-110vw).

Nota: ho aggiunto un altro 10vw al codice tipico fuori schermo di -100vw, per garantire che box-shadow della navigazione laterale non faccia capolino nell'area visibile principale quando è nascosto.

@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);
  }
}
Navigazione laterale in

Quando l'elemento #sidenav corrisponde a :target, imposta la posizione translateX() sulla homebase 0 e osserva come CSS scorre l'elemento dalla posizione fuori -110vw alla posizione "in" 0 su var(--duration) quando l'hash dell'URL viene modificato.

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

Visibilità delle transizioni

L'obiettivo adesso è nascondere il menu agli screen reader quando è spento, in modo che i sistemi non concentrino l'attenzione su un menu fuori schermo. Posso farlo impostando una transizione di visibilità quando :target cambia.

  • Quando entri, non effettuare la transizione della visibilità. Sii subito visibile in modo che possa vedere l'elemento scorrere e accettare lo stato attivo.
  • Quando esci, la visibilità delle transizioni è ritardata, quindi al termine dell'uscita il passaggio passa a hidden.

Miglioramenti dell'esperienza utente per l'accessibilità

Questa soluzione si basa sulla modifica dell'URL per la gestione dello stato. Ovviamente, l'elemento <a> dovrebbe essere utilizzato qui e offre senza costi alcune interessanti funzioni di accessibilità. Adoriamo i nostri elementi interattivi con etichette che indicano chiaramente le intenzioni degli utenti.

<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>
Una demo della voce fuori campo e dell'esperienza utente relativa all'interazione con la tastiera.

Ora i nostri pulsanti di interazione principali indicano chiaramente il loro intento sia per il mouse che per la tastiera.

:is(:hover, :focus)

Questo pratico pseudo-selettore funzionale CSS ci consente di essere inclusivi rapidamente con gli stili al passaggio del mouse, condividendoli anche con lo stato attivo.

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

Aggiungi elementi su JavaScript

Premi escape per chiudere

Il tasto Escape sulla tastiera dovrebbe chiudere il menu. Colleghiamolo.

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

sidenav.addEventListener('keyup', event => {
  if (event.code === 'Escape') document.location.hash = '';
});
Cronologia del browser

Per impedire che le interazioni di apertura e chiusura sovrappongano più voci nella cronologia del browser, aggiungi il seguente codice JavaScript incorporato al pulsante di chiusura:

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

In questo modo, la voce della cronologia degli URL verrà rimossa alla chiusura, come se il menu non fosse mai stato aperto.

Concentrati sull'esperienza utente

Lo snippet successivo ci aiuta a mettere in evidenza i pulsanti di apertura e chiusura dopo l'apertura o la chiusura. Voglio semplificare l'attivazione/disattivazione.

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

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

Quando si apre la barra di navigazione laterale, imposta lo stato attivo sul pulsante di chiusura. Quando la navigazione laterale si chiude, imposta lo stato attivo sul pulsante Apri. Per farlo, richiamo focus() sull'elemento in JavaScript.

Conclusione

Ora che sai come ci ho fatto, come lo faresti?! Ciò rende l'architettura dei componenti divertente. Chi produrrà la prima versione con gli slot? 🙂

Diversificaamo i nostri approcci e impariamo tutti i modi per creare sul web. Crea un Glitch, inviaci un tweet con la tua versione e lo aggiungerò alla sezione Remix della community qui sotto.

Remix della community