Una panoramica di base su come creare una barra di navigazione laterale a scorrimento adattabile
In questo post vorrei condividere con te come ho prototipato un componente Sidenav per il web che sia reattivo, stateful, supporti la navigazione da tastiera, funzioni con e senza JavaScript e funzioni su tutti i browser. Prova la demo.
Se preferisci i video, ecco una versione di YouTube di questo post:
Panoramica
È difficile creare un sistema di navigazione reattivo. Alcuni utenti useranno una tastiera, altri un computer potente e altri ancora un piccolo dispositivo mobile. Chiunque visiti il sito deve essere in grado di aprire e chiudere il menu.
Tattiche web
In questa esplorazione dei componenti ho avuto il piacere di combinare alcune funzionalità fondamentali della piattaforma web:
- CSS
:target
- Griglia di CSS
- CSS transforms
- Query multimediali CSS per area visibile e preferenze degli utenti
- JS per
focus
miglioramenti UX
La mia soluzione ha una barra laterale e attiva/disattiva solo quando l'area visibile "per dispositivi mobili" è pari o inferiore a 540px
.
540px
sarà il nostro punto di interruzione per passare dal layout interattivo mobile al layout desktop statico.
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 trovare corrispondenze con l'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>
Se fai clic su ciascuno di questi link viene modificato lo stato hash dell'URL della nostra pagina, dopodiché mostro e nascondo la barra laterale con una pseudo-classe:
@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 o con posizione fissa. La griglia, però, con la sintassi grid-area
,
consente di assegnare più elementi alla stessa riga o colonna.
Impilati
L'elemento principale del layout #sidenav-container
è una griglia che crea una riga e due colonne, di cui una è denominata stack
. Quando lo spazio è vincolato, CSS assegna a tutti gli elementi secondari dell'elemento <main>
lo stesso nome di griglia, posizionando tutti gli elementi nello stesso spazio e creando uno stack.
#sidenav-container {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
min-height: 100vh;
}
@media (max-width: 540px) {
#sidenav-container > * {
grid-area: stack;
}
}
Sfondo del menu
<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]
, utilizzato per chiudere il menu.
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
Regola 2fr
e 1fr
per trovare le proporzioni che preferisci per l'overlay del menu e il pulsante di chiusura dello spazio negativo.
Trasformazioni e transizioni 3D CSS
Il nostro layout ora include le dimensioni dell'area visibile su dispositivi mobili. Fino a quando non aggiungo nuovi stili, l'articolo viene sovrapposto per impostazione predefinita. Ecco alcune UX che sto cercando nella prossima sezione:
- Animazione apertura e chiusura
- Animazione solo con movimento se l'utente è d'accordo
- Animazione di
visibility
in modo che lo stato attivo della tastiera non entri nell'elemento fuori schermo
Quando inizio a implementare le animazioni in movimento, vorrei dare l'importanza all'accessibilità.
Movimenti accessibili
Non tutti vogliono avere un'esperienza di movimento a scorrimento. Nella nostra soluzione, questa preferenza viene applicata modificando una variabile CSS --duration
all'interno di una query supporti. Questo valore di query supporti rappresenta la preferenza del sistema operativo di un utente per il movimento (se disponibile).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Ora, quando la barra di navigazione laterale scorre in modo che sia aperta e chiusa, se un utente preferisce un movimento ridotto, sposto subito l'elemento in vista, mantenendo lo stato senza movimento.
Transizione, trasformazione, traduzione
Navigazione laterale in uscita (impostazione predefinita)
Per impostare lo stato predefinito della barra di navigazione laterale sui dispositivi mobili su uno stato fuori schermo, posizione l'elemento con transform: translateX(-110vw)
.
Nota: ho aggiunto un altro 10vw
al codice tipico fuori schermo di -100vw
,
per assicurarmi che box-shadow
della barra di navigazione laterale non sbirci nell'area visibile principale quando è nascosta.
@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()
sul homebase 0
e osserva come CSS far scorrere l'elemento dalla sua posizione esterna di -110vw
alla posizione "in" di 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à della transizione
L'obiettivo ora è nascondere il menu agli screen reader quando non è disponibile, in modo che i sistemi non impostino lo stato attivo su un menu fuori schermo. A questo scopo, imposto una
transizione di visibilità quando cambia :target
.
- Quando entrerai, non eseguire la transizione della visibilità; sii visibile subito, in modo che io possa vedere l'elemento scorrere e accettare lo stato attivo.
- Quando esci, la visibilità della transizione viene ritardata, quindi passa a
hidden
alla fine della transizione.
Miglioramenti dell'UX per l'accessibilità
Link
Questa soluzione si basa sulla modifica dell'URL per gestire lo stato.
Ovviamente, l'elemento <a>
dovrebbe essere utilizzato qui e ottiene senza costi alcune utili funzioni di accessibilità. Abbelliamo i nostri elementi interattivi con etichette che esprimono chiaramente l'intento.
<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>
Ora i nostri pulsanti di interazione principali indicano chiaramente la loro intenzione sia per il mouse che per la tastiera.
:is(:hover, :focus)
Questo pratico pseudo-selettore CSS ci consente di essere rapidamente inclusivi con gli stili al passaggio del mouse, condividendoli anche in modo specifico.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Cospargere su JavaScript
Premi escape
per chiudere
Il tasto Escape
sulla tastiera dovrebbe chiudere il menu, giusto? Collega il cavo.
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', event => {
if (event.code === 'Escape') document.location.hash = '';
});
Cronologia del browser
Per impedire che l'interazione di apertura e chiusura accumuli 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>
La voce della cronologia degli URL verrà rimossa alla chiusura, rendendola come se il menu non fosse mai stato aperto.
Focus UX
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.
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, attiva lo stato attivo sul pulsante di chiusura. Quando la barra di navigazione laterale si chiude,
attiva il pulsante Apri. Per farlo, chiamo focus()
sull'elemento in JavaScript.
Conclusione
Ora che sai come ci sono riuscito, come faresti?! Questo rende l'architettura dei componenti divertente. Chi realizzerà la prima versione con gli slot? 🙂
Diversifica 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 di seguito.
Remix della community
- @_developit con elementi personalizzati: demo e codice
- @mayeedwin1 con HTML/CSS/JS: demo e codice
- @a_nurella con un remix di Glitch: demo e codice
- @EvroMalarkey con HTML/CSS/JS: demo e codice