Codelab: Compila un componente de navegación lateral

En este codelab, aprenderás a compilar un componente de diseño de navegación lateral de deslizamiento responsivo en la Web. Compilaremos el componente sobre la marcha. Comenzaremos con HTML, luego CSS y, por último, JavaScript.

Consulta mi entrada de blog sobre Cómo compilar un componente de Sidenav para obtener información sobre las funciones de la plataforma web de CSS elegidas para compilar 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 puedas trabajar.

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

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

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

Luego, 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 cercano 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 funcionan muy bien dentro de los elementos <nav>, y los elementos <nav> quedan muy bien en las barras laterales de <aside>. Sin embargo, hay más por lo que podemos mejorar.

En el elemento de contenido principal, agrega un encabezado y un artículo para conservar de manera semántica 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 un vínculo de menú para abrir. En el lado, se encuentra el botón de cierre. Pronto mostraremos y ocultaremos elementos según el tamaño de la viewport.

En el elemento <article>, pegamos una oración de marcador de posición. Reemplaza `` con tus propios elementos o pega 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, es lo que hará que se pueda desplazar la página cuando supere la altura de tu viewport.

Hasta ahora, agregaste un elemento a un lado, con una navegación, vínculos y una manera de cerrarla. También agregaste un encabezado, una forma de abrir la barra de navegación lateral y un artículo al elemento principal. Esto es claro, semántico y bastante atemporal, pero podemos hacer que sea más limpio y claro para todos. El enlace abierto en la 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 cerrado 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 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 el navegador 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> muestre los elementos secundarios.

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

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

En esencia, este diseño dice lo siguiente: Crea una fila con nombre stack con todo su contenido y 2 columnas en esa fila, la 2a de las cuales también se llama stack. El tamaño de la primera columna debe ajustarse según sus necesidades de contenido mínimas, y la segunda columna puede ocupar el resto. Luego, si usas un viewport restringido de 540px o menos, coloca los elementos de contenido principal y de 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 apilado 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 de la navegación lateral.

Vuelve a actualizar el elemento <aside> en app/index.html:

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

Esto permite que el CSS haga coincidir 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 la URL que configuraremos con etiquetas <a>.

Además, para facilitar la segmentación de JavaScript, agrega IDs para los elementos clave que controlan la navegación lateral. Primero, agrega un ID al vínculo abierto en la navegación 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 de cierre de la navegación 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 <body> de la macro y nos vincula a la barra de URL. Continuemos.

El <aside> también tiene un diseño prolijo. Tiene 2 elementos secundarios: una <nav>, que es el componente con apariencia de papel que se desliza hacia afuera, y un elemento de vínculo <a> de cierre que establece la URL como #. El vínculo es invisible a la derecha del panel de navegación de deslizamiento hacia afuera del papel; es para que las personas puedan "hacer clic" en el componente visual y descartarlo.

Agrega el siguiente CSS a css/sidenav.css:

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

Pensé que la relación y los nombres eran un toque muy agradable aquí, donde la cuadrícula podía brillar y dar mucho control a un diseñador.

A continuación, debo superponer condicionalmente el contenido principal y mantener mi posición durante cualquier desplazamiento por el documento. Es un gran trabajo para position: sticky y overscroll-behavior.

Agrega los siguientes estilos para la navegación 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 diseños garantizan que la barra de navegación lateral sea la altura del viewport, se desplace verticalmente y contenga el desplazamiento. Lo más importante es que oculta el elemento. De forma predeterminada, cuando el viewport sea 540px o más pequeño, oculta ese panel de navegación lateral. A menos que

Agrega un seudoselector :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. Abre el menú lateral después de desplazarte por la página o intenta desplazarte por la página mientras la barra de navegación lateral está abierta. ¿Qué piensa?

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 diseños se orientan a nuestros botones de apertura y cierre, especifican sus estilos de presión y tacto y también los oculta cuando las viewports son 540px o más grandes.

Para darle un toque especial, agreguemos transformaciones de CSS con una 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 medios `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 = '';
  }
});

Esto detecta un evento de tecla en el elemento de navegación lateral. Si aparece como Escape, establece el hash de la URL en vacío, lo que hace la transición de la navegación lateral.

La siguiente parte de UX JS es la administración del enfoque. Quiero facilitar la apertura y el cierre, así que espero hasta que la navegación lateral haya finalizado una transición de algún tipo y, luego, la cotejo con el hash de la URL para determinar si está dentro o fuera. Luego, uso JavaScript para enfocar el botón complementario al que acaba de presionar.

Agrega el siguiente 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

Este es un resumen de las necesidades que tenía el componente. No dudes en desarrollarla, usarla con el estado de JavaScript en lugar de la URL y, en general, personalizarla. Siempre hay más para agregar o más casos de uso por tratar.

Abre css/brandnav.css para ver los estilos no relacionados con el diseño que apliqué a este componente. No me pareció importante para el conjunto de atributos en el que me estaba enfocando y esperaba que separar los estilos del diseño fomentara la acción de copiar y pegar. Podría haber más aprendizaje para ti ahí.

¿Cómo se hace para que se deslicen los componentes de navegación lateral responsivos? ¿Alguna vez tienes más de 1, como uno en ambos lados? Me encantaría que tu solución aparezca en un video de YouTube. Asegúrate de twittearme o comentar en YouTube con tu código. ¡Ayudará a todos!