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 compilar este componente.

Configuración

  1. Haz clic en Remix para editar para que el proyecto se pueda editar.
  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 de cierre 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>. Aun así, 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. Esto ya es limpio, semántico y bastante atemporal, pero podemos hacerlo más limpio y claro para todos. El enlace abierto en el panel de navegación 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 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 de cierre en la barra de navegación lateral podría marcarse con mayor claridad. Agrega los atributos title y aria-label al elemento de vínculo de cierre de navegación 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 la navegación 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 los elementos de contenido principal y la navegación lateral en la misma fila y columna, lo que hará que queden uno encima del otro en una cuadrícula de 1 x 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 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>

Esto abarca el diseño de apilamiento responsivo de la macro <body> y 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 ventana de visualización, que se desplace verticalmente y que contenga el desplazamiento. Es muy importante porque oculta el elemento. De forma predeterminada, cuando el viewport es 540px o más pequeño, oculta esa navegación 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 tacto, 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 JavaScript.

La tecla Escape debería cerrar el menú. Agrega este 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 la URL como vacío y realiza la transición del navegador lateral fuera de ella.

La siguiente pieza de JS de UX es la gestión del 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 completapantalla 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 allí.

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