Descripción general básica de cómo crear una navegación lateral desplegable responsiva
En esta publicación, quiero compartir contigo cómo creé un prototipo de un componente de barra lateral de navegación para la Web que es responsivo, tiene estado, admite la navegación con el teclado, funciona con JavaScript y sin él, y es compatible con todos los navegadores. Prueba la demostración.
Si prefieres un video, aquí tienes una versión de este artículo en YouTube:
Descripción general
Es difícil crear un sistema de navegación responsivo. Algunos usuarios usarán un teclado, otros tendrán computadoras de escritorio potentes y otros visitarán el sitio desde un dispositivo móvil pequeño. Todos los visitantes deben poder abrir y cerrar el menú.
Tácticas web
En esta exploración de componentes, tuve el placer de combinar algunas funciones críticas de la plataforma web:
- CSS
:target
- Cuadrícula de CSS
- Transformaciones de CSS
- Consultas de medios CSS para el viewport y las preferencias del usuario
- JS para
focus
Mejoras en la UX
Mi solución tiene una barra lateral y se activa solo cuando se encuentra en un viewport "móvil" de 540px
o menos.
540px
será nuestro punto de interrupción para alternar entre el diseño interactivo para dispositivos móviles y el diseño estático para computadoras.
Pseudoclase :target
de CSS
Un vínculo <a>
establece el hash de la URL en #sidenav-open
y el otro en vacío (''
). Por último, un elemento tiene el id
para que coincida con el hash:
<a href="#sidenav-open" id="sidenav-button" title="Open Menu" aria-label="Open Menu">
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<aside id="sidenav-open">
…
</aside>
Cuando haces clic en cada uno de estos vínculos, cambia el estado de hash de la URL de nuestra página. Luego, con una seudoclase, muestro y oculto la navegación lateral:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
Cuadrícula de CSS
En el pasado, solo usaba diseños y componentes de navegación lateral con posición absoluta o fija. Sin embargo, la cuadrícula, con su sintaxis grid-area
, nos permite asignar varios elementos a la misma fila o columna.
Pilas
El elemento de diseño principal #sidenav-container
es una cuadrícula que crea 1 fila y 2 columnas, 1 de cada una se llama stack
. Cuando el espacio es limitado, CSS asigna todos los elementos secundarios del elemento <main>
al mismo nombre de cuadrícula, lo que coloca todos los elementos en el mismo espacio y crea una pila.
#sidenav-container {
display: grid;
grid: [stack] 1fr / min-content [stack] 1fr;
min-height: 100vh;
}
@media (max-width: 540px) {
#sidenav-container > * {
grid-area: stack;
}
}
Fondo del menú
El <aside>
es el elemento de animación que contiene la navegación lateral. Tiene 2 elementos secundarios: el contenedor de navegación <nav>
llamado [nav]
y un fondo <a>
llamado [escape]
, que se usa para cerrar el menú.
#sidenav-open {
display: grid;
grid-template-columns: [nav] 2fr [escape] 1fr;
}
Ajusta 2fr
y 1fr
para encontrar la proporción que te guste para la superposición del menú y su botón de cierre de espacio negativo.
Transiciones y transformaciones en 3D de CSS
Nuestro diseño ahora se apila en un tamaño de viewport para dispositivos móviles. Hasta que agregue algunos estilos nuevos, se superpondrá a nuestro artículo de forma predeterminada. En esta próxima sección, quiero lograr la siguiente UX:
- Animar la apertura y el cierre
- Solo se debe animar con movimiento si el usuario lo acepta.
- Anima
visibility
para que el enfoque del teclado no ingrese al elemento fuera de la pantalla
Cuando comience a implementar animaciones de movimiento, quiero tener en cuenta la accesibilidad desde el principio.
Movimiento accesible
No todos querrán una experiencia de movimiento deslizable. En nuestra solución, esta preferencia se aplica ajustando una variable de CSS --duration
dentro de una consulta de medios. Este valor de la consulta de medios representa la preferencia del sistema operativo del usuario por el movimiento (si está disponible).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Ahora, cuando nuestro sidenav se desliza para abrirse y cerrarse, si un usuario prefiere un movimiento reducido, muevo instantáneamente el elemento a la vista, manteniendo el estado sin movimiento.
Transición, transformación y traducción
Panel lateral hacia afuera (predeterminado)
Para establecer el estado predeterminado de nuestro sidenav en dispositivos móviles como un estado fuera de la pantalla, posiciono el elemento con transform: translateX(-110vw)
.
Ten en cuenta que agregué otro 10vw
al código fuera de pantalla típico de -100vw
para asegurarme de que el box-shadow
de la navegación lateral no se asome a la ventana principal cuando esté oculta.
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
transform: translateX(-110vw);
will-change: transform;
transition:
transform var(--duration) var(--easeOutExpo),
visibility 0s linear var(--duration);
}
}
Navegación lateral en
Cuando el elemento #sidenav
coincide con :target
, establece la posición translateX()
en la posición base 0
y observa cómo CSS desliza el elemento desde su posición externa de -110vw
hasta su posición "interna" de 0
durante var(--duration)
cuando se cambia el hash de la URL.
@media (max-width: 540px) {
#sidenav-open:target {
visibility: visible;
transform: translateX(0);
transition:
transform var(--duration) var(--easeOutExpo);
}
}
Visibilidad de la transición
El objetivo ahora es ocultar el menú de los lectores de pantalla cuando no esté visible, de modo que los sistemas no enfoquen un menú fuera de la pantalla. Para lograr esto, establezco una transición de visibilidad cuando cambia :target
.
- Cuando ingreses, no hagas una transición de visibilidad; sé visible de inmediato para que pueda ver el elemento deslizarse y aceptar el enfoque.
- Cuando sale, la visibilidad de la transición se retrasa, por lo que cambia a
hidden
al final de la transición de salida.
Mejoras en la UX de accesibilidad
Vínculos
Esta solución se basa en cambiar la URL para que se pueda administrar el estado.
Naturalmente, aquí se debe usar el elemento <a>
, que obtiene algunas funciones de accesibilidad agradables de forma gratuita. Decoremos nuestros elementos interactivos con etiquetas que articulen claramente la intención.
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu"></a>
<a href="#sidenav-open" id="sidenav-button" class="hamburger" title="Open Menu" aria-label="Open Menu">
<svg>...</svg>
</a>
Ahora, nuestros botones de interacción principales indican claramente su intención tanto para el mouse como para el teclado.
:is(:hover, :focus)
Este práctico pseudoselector funcional de CSS nos permite ser inclusivos rápidamente con nuestros estilos de desplazamiento, ya que también los comparte con el enfoque.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Agrega JavaScript
Presiona escape
para cerrar.
La tecla Escape
del teclado debería cerrar el menú, ¿verdad? Conectemos eso.
const sidenav = document.querySelector('#sidenav-open');
sidenav.addEventListener('keyup', event => {
if (event.code === 'Escape') document.location.hash = '';
});
Historial del navegador
Para evitar que la interacción de abrir y cerrar apile varias entradas en el historial del navegador, agrega el siguiente código JavaScript intercalado al botón de cierre:
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>
Esto quitará la entrada del historial de URLs cuando se cierre, como si el menú nunca se hubiera abierto.
UX de enfoque
El siguiente fragmento nos ayuda a enfocar los botones de abrir y cerrar después de que se abren o cierran. Quiero que sea fácil activar o desactivar la opción.
sidenav.addEventListener('transitionend', e => {
const isOpen = document.location.hash === '#sidenav-open';
isOpen
? document.querySelector('#sidenav-close').focus()
: document.querySelector('#sidenav-button').focus();
})
Cuando se abra la navegación lateral, enfoca el botón de cierre. Cuando se cierre la barra de navegación lateral, enfoca el botón de apertura. Para ello, llamo a focus()
en el elemento en JavaScript.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías tú? Esto genera una arquitectura de componentes divertida. ¿Quién creará la primera versión con ranuras? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de crear en la Web. Crea un Glitch, envíame un tweet con tu versión y la agregaré a la sección de Remixes de la comunidad que se encuentra más abajo.
Remixes de la comunidad
- @_developit con elementos personalizados: demostración y código
- @mayeedwin1 con HTML/CSS/JS: demostración y código
- @a_nurella con un remix de Glitch: demostración y código
- @EvroMalarkey con HTML/CSS/JS: demostración y código