Codelab: creazione di un componente di Barra di navigazione laterale

Questo codelab ti insegna a creare un componente di layout di navigazione laterale scorrevole adattabile sul web. Lo creeremo man mano, iniziando con HTML, poi CSS e infine JavaScript.

Dai un'occhiata al mio post del blog Creare un componente Sidenav per scoprire le funzionalità della piattaforma web CSS scelte per la creazione di questo componente.

Configurazione

  1. Fai clic su Remix per modificare per rendere il progetto modificabile.
  2. Apri app/index.html.

HTML

Prima di tutto, prendi le nozioni di base per la configurazione HTML, in modo da avere contenuti e alcune caselle con cui lavorare.

Inserisci il seguente codice HTML nel tag <body>.

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

<aside> contiene il menu di navigazione come elemento complementare a <main>, che contiene i contenuti della pagina principale.

Successivamente, completeremo questi elementi semantici con il resto dei contenuti della pagina.

Aggiungi un elemento di navigazione, alcuni link di navigazione e un link di chiusura all'interno dell'elemento <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>

I link sono molto efficaci all'interno degli elementi <nav>, mentre gli elementi <nav> sono molto efficaci nelle barre laterali di <aside>. Tuttavia, possiamo fare di più per migliorare.

Nell'elemento di contenuto principale, aggiungi un'intestazione e un articolo per contenere semanticamente i contenuti del layout.

<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'intestazione contiene il link di apertura del menu. L'altro lato ha il pulsante di chiusura. A breve mostreremo e nasconderemo gli elementi in base alle dimensioni del viewport.

Nell'elemento <article> abbiamo incollato una frase segnaposto. Sostituisci " con i tuoi contenuti o incolla il lorem fornito di seguito:

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

Questi contenuti e la loro lunghezza fanno sì che la pagina sia scorrevole quando supera l'altezza dell'area visibile.

Finora hai aggiunto un elemento aside, con un menu di navigazione, link e un modo per chiudere il riquadro laterale. Hai anche aggiunto un'intestazione, un modo per aprire il riquadro laterale e un articolo all'elemento principale. È già chiaro, semantico e piuttosto senza tempo, ma possiamo renderlo più pulito e comprensibile per tutti. Il link aperto nella barra di navigazione potrebbe essere contrassegnato in modo più chiaro.

Aggiungi gli attributi title e aria-label all'elemento link di apertura dell'intestazione:

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

Anche l'icona SVG aperta potrebbe essere contrassegnata in modo più chiaro. Aggiungi i seguenti attributi all'elemento SVG all'interno dell'elemento link aperto:

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

Il link di chiusura nel riquadro laterale potrebbe essere contrassegnato in modo più chiaro. Aggiungi gli attributi title e aria-label all'elemento link di chiusura del riquadro laterale:

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

CSS

È il momento di disporre gli elementi. I contenuti principali e il riquadro laterale sono elementi secondari diretti del tag <body>, quindi è un buon punto di partenza.

Aggiungi il seguente CSS a css/sidenav.css in modo che l'elemento <body> mostri i contenuti secondari.

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

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

In sostanza, questo layout indica: Crea una riga denominata stack con tutti gli elementi al suo interno e 2 colonne in quella riga, la seconda delle quali è denominata anche stack. La prima colonna deve essere dimensionata in base alle sue esigenze minime di contenuto, mentre la seconda colonna può occupare il resto. Quindi, se l'area visibile è vincolata di massimo 540px, inserisci gli elementi di navigazione laterale e dei contenuti principali nella stessa riga e colonna in modo che si trovino uno sopra l'altro in una griglia 1 x 1.

Con questa funzionalità di accatastamento adattabile come base, ora possiamo sfruttare lo stato della barra degli URL per attivare/disattivare la visibilità e lo stile di transizione del riquadro laterale.

Aggiorna di nuovo l'elemento <aside> in app/index.html:

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

In questo modo, il CSS può associare un elemento e l'hash dell'URL. Questo è importante per l'utilizzo di :target. Ora l'ID dell'elemento può corrispondere all'hash dell'URL che imposteremo con i tag <a>.

Inoltre, per semplificare il targeting JavaScript, aggiungi gli ID per gli elementi chiave che controllano il riquadro laterale. Innanzitutto, aggiungi un ID al link di apertura del riquadro laterale:

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

Aggiungi un ID al link di chiusura del riquadro laterale:

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

Questo è il layout di impilamento responsive della macro <body>, che ci collega alla barra degli URL. Andiamo avanti.

Anche il <aside> ha un layout pulito. Ha due elementi secondari, un <nav>, ovvero il componente simile a un foglio di carta che si estrae, e un elemento link di chiusura <a> che imposta l'URL su #. Il link è invisibile a destra del menu a scomparsa di Carta; in questo modo gli utenti possono fare clic sul componente visivo per ignorarlo.

Aggiungi il seguente CSS a css/sidenav.css:

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

Il rapporto e i nomi sono stati un tocco davvero piacevole, in cui la griglia poteva dare il meglio di sé e offrire al designer un grande controllo.

Successivamente, devo sovrapporre condizionalmente i contenuti principali e mantenere la mia posizione durante lo scorrimento del documento. È un'ottima opportunità per position: sticky e alcuni overscroll-behavior.

Aggiungi i seguenti stili per il riquadro laterale:

#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 */
  }
}

Questi stili assicurano che la navigazione laterale corrisponda all'altezza dell'area visibile, che scorra verticalmente e contenga lo scorrimento. Molto importante, nasconde l'elemento. Per impostazione predefinita, nasconde il riquadro laterale quando l'area visibile è pari o inferiore a 540px. A meno che tu!

Aggiungi un pseudo-selettore :target all'elemento #sidenav-open:

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

Quando l'ID dell'elemento e della barra degli URL sono uguali, imposta visibility su visible. Apri il menu laterale dopo aver scorretto la pagina o prova a scorrere la pagina con il menu laterale aperto. Cosa ne pensi?

Aggiungi il seguente CSS alla parte inferiore di 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;
  }
}

Questi stili hanno come target i nostri pulsanti di apertura e chiusura, ne specificano gli stili di tocco e li nascondono anche quando le visualizzazioni sono pari o superiori a 540px.

Per un tocco in più, aggiungiamo le trasformazioni CSS con un'accessibilità rispettosa. Aggiungi il seguente CSS a 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;
  }
}
Una demo dell'interazione con e senza applicazione della durata in base alla query sui contenuti multimediali "prefers-reduced-motion".

Aggiungi un po' di JavaScript

Il tasto Escape dovrebbe chiudere il menu. Aggiungi questo codice JS a js/index.js:

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

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

Questo ascolta un evento chiave nell'elemento sidenav. Se è Escape, l'hash dell'URL viene impostato su vuoto, con la transizione della barra laterale in uscita.

Questo altro componente di UX JS è la gestione dell'attenzione. Voglio semplificare l'apertura e la chiusura, quindi aspetto fino a quando il riquadro laterale non ha completato una transizione di qualche tipo, poi lo verifico con l'hash dell'URL per determinare se è attivo o meno. Poi utilizzo JavaScript per impostare il focus sul pulsante complementare a quello appena premuto.

Aggiungi il seguente codice JavaScript a 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();
});

Prova

  • Per visualizzare l'anteprima del sito, premi Visualizza app. Quindi premi A schermo intero schermo intero.

Conclusione

Ecco un riepilogo delle mie esigenze relative al componente. Non esitare a migliorarla, a gestirla con lo stato JavaScript anziché con l'URL e, in generale, a farla tua. C'è sempre altro da aggiungere o altri casi d'uso da trattare.

Apri css/brandnav.css per controllare gli stili non correlati al layout che ho applicato a questo componente. Non ritenevo che fosse importante per il set di funzionalità su cui mi stavo concentrando e speravo che la separazione degli stili dal layout avrebbe incoraggiato la copia e incolla. Potrebbe esserci altro apprendimento per te lì.

Come creare componenti sidenav adattabili scorrevoli? Hai mai più di una, ad esempio una su entrambi i lati? Mi piacerebbe mostrare la tua soluzione in un video di YouTube. Assicurati di inviarmi un tweet o di commentare su YouTube con il tuo codice, sarà di aiuto a tutti.