Codelab: Compila un componente de navegación lateral

En este codelab, aprenderás a compilar un componente de diseño de navegación lateral deslizante responsivo en la Web. Compilaremos el componente a medida que avancemos, comenzando con HTML, luego CSS y, finalmente, JavaScript.

Consulta mi entrada de blog Cómo crear un componente de Sidenav para obtener información sobre las funciones de la plataforma web de CSS que se eligieron para crear este componente.

Configuración

  1. Haz clic en Remix to Edit para que el proyecto sea editable.
  2. Abre app/index.html.

HTML

Primero, obtén los aspectos básicos de la configuración de HTML para que haya contenido y algunos cuadros con los que trabajar.

Coloca el siguiente código HTML en la etiqueta <body>.

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

El <aside> contiene el menú de navegación como un elemento complementario de <main>, que contiene el contenido principal de la página.

A continuación, completaremos esos elementos semánticos con el resto del contenido de la página.

Agrega un elemento de navegación, algunos vínculos de navegación y un vínculo para cerrar dentro del 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>

Los vínculos se ven muy bien dentro de los elementos <nav>, y los elementos <nav> se ven muy bien en las barras laterales <aside>. Sin embargo, podemos hacer más para mejorar.

En el elemento de contenido principal, agrega un encabezado y un artículo para contener semánticamente el contenido del diseño.

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

El encabezado tiene el vínculo para abrir el menú. El panel lateral tiene el botón de cierre. Pronto mostraremos y ocultaremos elementos según el tamaño del viewport.

En el elemento <article>, pegamos una oración de marcador de posición. Reemplaza " por tu propio contenido o pega el lorem que se proporciona a continuación:

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

Este contenido y su longitud son los que harán que la página se pueda desplazar cuando supere la altura del viewport.

Hasta ahora, agregaste un elemento lateral, con un menú de navegación, vínculos y una forma de cerrar el panel lateral. También agregaste un encabezado, una forma de abrir el panel lateral y un artículo al elemento principal. Ya es claro, semánticamente correcto y atemporal, pero podemos hacerlo más claro y comprensible para todos. El vínculo abierto en el panel lateral podría marcarse con mayor claridad.

Agrega los atributos title y aria-label al elemento de vínculo abierto del encabezado:

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

El ícono de SVG abierto también podría marcarse con mayor claridad. Agrega los siguientes atributos al SVG dentro del elemento de vínculo abierto:

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

El vínculo para cerrar en el panel lateral podría estar marcado con mayor claridad. Agrega los atributos title y aria-label al elemento de vínculo de cierre del panel lateral:

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

CSS

Es hora de diseñar los elementos. El contenido principal y el panel lateral son elementos secundarios directos de la etiqueta <body>, por lo que es un buen punto de partida.

Agrega el siguiente CSS a css/sidenav.css para que el elemento <body> distribuya los elementos secundarios.

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

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

Este diseño dice, en esencia: Crea una fila llamada stack con todo lo que contiene y 2 columnas en esa fila, la segunda de las cuales también se llama stack. El tamaño de la 1ª columna debe basarse en sus necesidades de contenido mínimas, y la 2ª columna puede ocupar el resto. Luego, si tienes un viewport restringido de 540px o menos, coloca el panel lateral y los elementos del contenido principal en la misma fila y columna, de modo que se superpongan en una cuadrícula de 1 × 1.

Con esta funcionalidad de apilamiento responsivo como base, ahora podemos aprovechar el estado de la barra de URL para activar o desactivar la visibilidad y el estilo de transición del panel lateral.

Actualiza el elemento <aside> en app/index.html:

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

Esto permite que el CSS coincida con un elemento y el hash de la URL. Esto es importante para el uso de :target. Ahora, el ID del elemento puede coincidir con el hash de URL que estableceremos con las etiquetas <a>.

Además, para facilitar la segmentación de JavaScript, agrega IDs para los elementos clave que controlan el panel lateral. Primero, agrega un ID al vínculo de apertura del panel lateral:

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

A continuación, agrega un ID al vínculo para cerrar el panel lateral:

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

Eso concluye el diseño de apilamiento responsivo de la macro <body> y, además, nos vincula a la barra de URL. Continuemos.

El <aside> también tiene un diseño ordenado. Tiene 2 elementos secundarios: un <nav>, que es el componente con aspecto de papel que se desliza hacia afuera, y un elemento de vínculo <a> de cierre que establece la URL en #. El vínculo es invisible a la derecha de la navegación lateral deslizante de papel, de modo que las personas puedan hacer clic en el componente visual para descartarlo.

Agrega el siguiente CSS a css/sidenav.css:

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

Creo que la proporción y los nombres fueron un toque muy agradable aquí, en el que la cuadrícula podría brillar y darle al diseñador mucho control.

A continuación, debo superponer condicionalmente el contenido principal y conservar mi posición a través de cualquier desplazamiento del documento. Este es un gran trabajo para position: sticky y algunos overscroll-behavior.

Agrega los siguientes estilos para el panel lateral:

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

Esos estilos garantizan que el panel lateral sea la altura de la vista, se desplace verticalmente y contenga el desplazamiento. Es muy importante porque oculta el elemento. De forma predeterminada, cuando el viewport es 540px o menor, se oculta ese panel lateral. A menos que

Agrega un pseudoselector :target al elemento #sidenav-open:

#sidenav-open {

  @media (max-width: 540px) {

    &:target {
      visibility: visible;
    }
  }
}

Cuando el ID de ese elemento y la barra de URL sean iguales, establece visibility en visible. Continúa y abre el menú lateral después de desplazarte por la página o intenta desplazarte por la página mientras el panel lateral está abierto. ?

Agrega el siguiente CSS a la parte inferior 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;
  }
}

Estos estilos se orientan a nuestros botones de apertura y cierre, especifican sus estilos de presión y toque, y también los ocultan cuando los viewports son 540px o más grandes.

Para darle un poco de estilo, agreguemos transformaciones de CSS con accesibilidad respetuosa. Agrega el siguiente 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 demostración de la interacción con y sin duración aplicada según la consulta de contenido multimedia "prefers-reduced-motion".

Agrega un poco de JavaScript

La tecla Escape debería cerrar el menú. Agrega este código JS a js/index.js:

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

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

Detecta un evento de tecla en el elemento del panel lateral. Si es Escape, establece el hash de URL en vacío, lo que hace que el panel lateral realice la transición.

El siguiente elemento de UX JS es la administración de enfoque. Quiero facilitar la apertura y el cierre, por lo que espero hasta que el panel lateral haya terminado una transición de algún tipo y, luego, lo comparo con el hash de URL para determinar si está dentro o fuera. Luego, uso JavaScript para establecer el enfoque en el botón complementario al que acaba de presionar.

Agrega el siguiente código 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();
});

Probar

  • Para obtener una vista previa del sitio, presiona Ver app. Luego, presiona Pantalla completa pantalla completa.

Conclusión

Eso es todo sobre las necesidades que tenía con el componente. No dudes en basarte en él, impulsarlo con el estado de JavaScript en lugar de la URL y, en general, personalizarlo. Siempre hay más para agregar o más casos de uso para cubrir.

Abre css/brandnav.css para ver los estilos no relacionados con el diseño que apliqué a este componente. No sentí que fuera importante para el conjunto de funciones en el que me enfocaba y esperaba que separar los estilos del diseño fomentara el copiar y pegar. Podrías aprender mucho más.

¿Cómo se crean componentes de barra lateral responsivos deslizables? ¿Alguna vez tienes más de 1, como uno en ambos lados? Me encantaría mostrar tu solución en un video de YouTube. Asegúrate de tuitearme o comentar en YouTube con tu código. Eso ayudará a todos.