Cómo compilar un componente de rutas de navegación

Una descripción general fundamental sobre cómo crear un componente de rutas de navegación responsivo y accesible para que los usuarios naveguen por tu sitio

En esta publicación, quiero compartir ideas sobre cómo crear componentes de rutas de navegación. Prueba la demostración.

. Demostración

Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:

Descripción general

El componente rutas de navegación muestra en qué parte de la jerarquía del sitio se encuentra el usuario. El nombre es de Hansel y Gretel, que dejó caer migas detrás de ellos en algunos bosques oscuros bosques y lograron llegar a casa al rastrear las migas hacia atrás.

Las rutas de navegación de esta entrada no son estándares rutas de navegación, son similares a una ruta de navegación. Ofrecen funciones adicionales, ya que colocan las páginas directamente en la navegación con un <select>, lo que brinda acceso a varios niveles como sea posible.

UX en segundo plano

En el video de demostración del componente anterior, las categorías de marcador de posición son géneros de videojuegos. Este sendero se crea navegando por la siguiente ruta: home » rpg » indie » on sale, como se muestra a continuación.

Este componente de ruta de navegación debe permitir a los usuarios desplazarse por este jerarquía de la información; saltando ramas y seleccionando páginas con velocidad y exactitud.

Arquitectura de la información

Me parece útil pensar en las colecciones y los elementos.

Colecciones

Una colección es un array de opciones entre las que se puede elegir. En la página de inicio de en el prototipo de ruta de navegación de esta publicación, las colecciones son FPS, RPG, brawler, mazmorras, deportes y acertijos.

Elementos

Un videojuego es un artículo, una colección específica también podría ser un elemento si representa a otra colección. Por ejemplo, un RPG es un elemento y un rol de elementos no utilizados. Cuando es un elemento, el usuario está en esa página de colección. Por ejemplo: están en la página de RPG, que muestra una lista de juegos de RPG, incluido el subcategorías adicionales AAA, independientes y autopublicadas.

En términos de informática, este componente de rutas de navegación representa un multidimensional array:

const rawBreadcrumbData = {
  "FPS": {...},
  "RPG": {
    "AAA": {...},
    "indie": {
      "new": {...},
      "on sale": {...},
      "under 5": {...},
    },
    "self published": {...},
  },
  "brawler": {...},
  "dungeon crawler": {...},
  "sports": {...},
  "puzzle": {...},
}

Tu aplicación o sitio web tendrá una arquitectura de la información (AI) personalizada que crea una es un array multidimensional diferente, pero espero que el concepto de destino de la recopilación las páginas y el recorrido de jerarquía también pueden llegar a tus rutas de navegación.

Diseños

Marca

Los componentes buenos comienzan con el HTML adecuado. En esta próxima sección, las opciones de lenguaje de marcado y cómo afectan al componente en general.

Esquema oscuro y claro

<meta name="color-scheme" content="dark light">

La metaetiqueta color-scheme de la tabla anterior El fragmento le informa al navegador que esta página desea usar el navegador claro y el oscuro. estilos. Las rutas de navegación de ejemplo no incluyen ninguna CSS para estos esquemas de colores, y, por lo tanto, las rutas de navegación usarán los colores predeterminados que proporciona el navegador.

<nav class="breadcrumbs" role="navigation"></nav>

Es apropiado utilizar la Elemento <nav> para la navegación del sitio, que tiene un rol implícito de ARIA navegación. Durante las pruebas, noté que tener el atributo role cambió la forma en que un cuando el lector de pantalla interactuaba con el elemento, en realidad, se anunció como la navegación, así que elegí agregarlo.

Íconos

Cuando un ícono se repite en una página, el archivo SVG Elemento <use> significa que puedes definir el objeto path una vez y usarlo para todas las instancias de la ícono. Esto evita que se repita la misma información de la ruta de acceso, lo que causa documentos más grandes y la posibilidad de que haya incoherencias en la ruta.

Para usar esta técnica, agrega un elemento SVG oculto a la página y ajusta los íconos En un elemento <symbol> con un ID único:

<svg style="display: none;">

  <symbol id="icon-home">
    <title>A home icon</title>
    <path d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/>
  </symbol>

  <symbol id="icon-dropdown-arrow">
    <title>A down arrow</title>
    <path d="M19 9l-7 7-7-7"/>
  </symbol>

</svg>

El navegador lee el SVG HTML, guarda la información del ícono en la memoria y continúa con el resto de la página y hace referencia al ID para usos adicionales de el ícono, como se muestra a continuación:

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-home" />
</svg>

<svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true">
  <use href="#icon-dropdown-arrow" />
</svg>

Herramientas para desarrolladores que muestran un elemento de uso de SVG renderizado.

Define una vez y úsala todas las veces que desees, con un impacto mínimo en el rendimiento de la página. y un estilo flexible. Observa que se agregó aria-hidden="true" al elemento SVG. Los íconos no son útiles para quienes navegan y solo escuchan el contenido, de esos usuarios evita que agreguen ruido innecesario.

Aquí es donde divergen la ruta de navegación tradicional y las de este componente. Por lo general, solo sería un vínculo de <a>, pero agregué una UX de recorrido con una seleccionar disfrazada. La clase .crumb se encarga de diseñar el vínculo y el ícono, mientras que .crumbicon se encarga de apilar el ícono y seleccionar de un elemento común. Lo llamé un enlace dividido porque sus funciones son muy similar a un split-button, sino para navegar por las páginas.

<span class="crumb">
  <a href="#sub-collection-b">Category B</a>
  <span class="crumbicon">
    <svg>...</svg>
    <select class="disguised-select" title="Navigate to another category">
      <option>Category A</option>
      <option selected>Category B</option>
      <option>Category C</option>
    </select>
  </span>
</span>

Un enlace y algunas opciones no tienen nada de especial, pero agregan más funcionalidad a un una ruta de navegación simple. Agregar un title al elemento <select> es útil para la pantalla usuarios lectores, brindándoles información sobre la acción del botón. Sin embargo, brinda la misma ayuda a todos los demás, verás que se destaca iPad Un atributo proporciona contexto del botón a muchos usuarios.

Captura de pantalla con el elemento de selección invisible sobre el que se coloca el cursor sobre el elemento
que se muestra información contextual.

Decoraciones de separador

<span class="crumb-separator" aria-hidden="true">→</span>

Los separadores son opcionales, agregar solo uno también funciona muy bien (mira el tercer ejemplo en el video). arriba). Luego, asigno a cada aria-hidden="true", ya que son decorativos y no algo que un lector de pantalla debe anunciar.

La propiedad gap, que se describe a continuación, hace que el espaciado de estos elementos sea directo.

Estilos

Dado que el color usa colores del sistema, se trata principalmente de espacios y pilas para los estilos.

Dirección y flujo del diseño

Herramientas para desarrolladores que muestran la alineación de la navegación de la ruta de navegación con una superposición de flexbox
.

El elemento de navegación principal nav.breadcrumbs establece una propiedad personalizada con alcance. para que lo usen los niños y establece un espacio horizontal . Esto garantiza que las migas, los divisores y los iconos se alineen.

.breadcrumbs {
  --nav-gap: 2ch;

  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  padding: calc(var(--nav-gap) / 2);
}

Una ruta de navegación que se muestra alineada verticalmente con superposiciones de flexbox.

Cada .crumb también establece un diseño horizontal alineado de forma vertical con algunos brecha, pero se orienta, en especial, a su vínculo secundario y especifica el estilo white-space: nowrap Esto es crucial para las rutas de navegación de varias palabras, ya que quieren que usen varias líneas. Más adelante en esta publicación, agregaremos estilos para manejar las desbordamiento horizontal que generó esta propiedad white-space

.crumb {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--nav-gap) / 4);

  & > a {
    white-space: nowrap;

    &[aria-current="page"] {
      font-weight: bold;
    }
  }
}

Se agrega aria-current="page" para que el vínculo de la página actual se destaque del resto el resto. Los usuarios de lectores de pantalla no solo tendrán un indicador claro para la página actual, hemos diseñado el elemento visualmente para ayudar a los usuarios videntes obtener una experiencia del usuario similar.

El componente .crumbicon usa una cuadrícula para apilar un ícono SVG con un mensaje invisibles" elemento <select>.

Se muestra Herramientas para desarrolladores de cuadrícula superpuesta en un botón en el que la fila y la columna están a la vez
de una pila con nombre.

.crumbicon {
  --crumbicon-size: 3ch;

  display: grid;
  grid: [stack] var(--crumbicon-size) / [stack] var(--crumbicon-size);
  place-items: center;

  & > * {
    grid-area: stack;
  }
}

El elemento <select> es el último en el DOM, por lo que está en la parte superior de la pila. e interactivo. Agrega un estilo de opacity: .01 para que el elemento aún se pueda usar. y el resultado es una casilla de selección que se ajusta perfectamente a la forma del icono. Esta es una buena manera de personalizar el aspecto de un elemento <select> mientras manteniendo la funcionalidad integrada.

.disguised-select {
  inline-size: 100%;
  block-size: 100%;
  opacity: .01;
  font-size: min(100%, 16px); /* Defaults to 16px; fixes iOS zoom */
}

Menú ampliado

Las rutas de navegación deben poder representar un recorrido muy largo. Me encanta permitir las cosas salieran de la pantalla horizontalmente, cuando corresponde, y sentí que el componente de rutas de navegación calificó bien.

.breadcrumbs {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: calc(var(--nav-gap) / 2);

  & > .crumb:last-of-type {
    scroll-snap-align: end;
  }

  @supports (-webkit-hyphens:none) { & {
    scroll-snap-type: none;
  }}
}

Los estilos de menú ampliado configuran la siguiente UX:

  • Desplazamiento horizontal con contención del sobredesplazamiento.
  • Relleno de desplazamiento horizontal.
  • Un punto de brote en la última miga. Esto significa que, cuando se carga la página, la primera cargas de migas a presión y a la vista.
  • Quita el punto de ajuste de Safari, que tiene dificultades con la posición horizontal el desplazamiento y el ajuste de combinaciones.

Consultas de medios

Un ajuste sutil para viewports más pequeños es ocultar la pantalla principal etiqueta, dejando solo el ícono:

@media (width <= 480px) {
  .breadcrumbs .home-label {
    display: none;
  }
}

Una al lado de las rutas de navegación con y sin una etiqueta de inicio, por
comparación.

Accesibilidad

Movimiento

No hay mucho movimiento en este componente, pero si envuelves la en una comprobación de prefers-reduced-motion, podemos evitar movimientos no deseados.

@media (prefers-reduced-motion: no-preference) {
  .crumbicon {
    transition: box-shadow .2s ease;
  }
}

No es necesario cambiar ninguno de los otros estilos, por lo que los efectos de desplazamiento y enfoque son geniales. y tiene sentido sin un transition, pero si el movimiento está bien, agregaremos una sutil transición a la interacción.

JavaScript

En primer lugar, sin importar el tipo de router que uses en tu sitio o aplicación, Cuando un usuario cambia las rutas de navegación, se debe actualizar la URL, y el usuario debe mostrar la página adecuada. En segundo lugar, para normalizar la experiencia del usuario, asegúrate de que No se producen navegaciones inesperadas cuando los usuarios solo están navegando. <select> opciones de estado.

JavaScript debe controlar dos medidas críticas de la experiencia del usuario: seleccionar tiene Prevención de activación de eventos de cambio modificados e urgentes de <select>.

La prevención de eventos inmediatos es necesaria debido al uso de un <select> . En Windows Edge, y probablemente en otros navegadores también, la opción changed seleccionada evento se activa cuando el usuario explora las opciones con el teclado. Por eso lo llamamos “rápido”, ya que el usuario solo seleccionó la opción pseudoseleccionó, como colocar el cursor sobre o enfoque, pero todavía no confirmó la elección con enter o click. Los ansiosos este evento hace que la función de cambio de categoría de componente sea inaccesible porque Abrir el cuadro de selección y navegar por un elemento activará el evento y cambiar la página, antes de que el usuario esté listo.

Un evento mejor de <select> modificado

const crumbs = document.querySelectorAll('.breadcrumbs select')
const allowedKeys = new Set(['Tab', 'Enter', ' '])
const preventedKeys = new Set(['ArrowUp', 'ArrowDown'])

// watch crumbs for changes,
// ensures it's a full value change, not a user exploring options via keyboard
crumbs.forEach(nav => {
  let ignoreChange = false

  nav.addEventListener('change', e => {
    if (ignoreChange) return
    // it's actually changed!
  })

  nav.addEventListener('keydown', ({ key }) => {
    if (preventedKeys.has(key))
      ignoreChange = true
    else if (allowedKeys.has(key))
      ignoreChange = false
  })
})

La estrategia para esto es mirar los eventos de presión del teclado en cada <select>. y determina si la tecla pulsada fue confirmación de navegación (Tab o Enter) o la navegación espacial (ArrowUp o ArrowDown). Con este el componente puede decidir esperar o salir, cuando el evento para el Se activa el elemento <select>.

Conclusión

Ahora que sabes cómo lo hice, ¿cómo lo harías?‽ 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crear una demostración, twittearme vínculos y la agregaré. a la sección de remixes de la comunidad.

Remixes de la comunidad