Descripción general de los conceptos básicos sobre cómo crear una barra lateral adaptable y deslizable
En esta publicación, quiero compartir contigo cómo generé un prototipo de un componente de Sidenav para la Web que es responsivo, con estado, admite la navegación con teclado, funciona con y sin JavaScript, y funciona en todos los navegadores. Prueba la demostración.
Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:
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 desde un dispositivo móvil pequeño. Todos los visitantes deberían poder abrir y cerrar el menú.
Tácticas web
En esta exploración de componentes, tuve la alegría de combinar algunas funciones fundamentales de la plataforma web:
- CSS
:target
- Cuadrícula de CSS
- transforms de CSS
- Consultas de medios de CSS para el viewport y las preferencias del usuario
- JS para
focus
mejoras de UX
Mi solución tiene una barra lateral y se activa solo en un viewport de "dispositivo móvil" de 540px
o menos.
540px
será nuestro punto de interrupción para cambiar entre el diseño interactivo para dispositivos móviles y el diseño estático para computadoras de escritorio.
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 hacer coincidir 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, se cambia el estado de hash de la URL de nuestra página y, luego, con una seudoclase I muestro y oculto el panel lateral:
@media (max-width: 540px) {
#sidenav-open {
visibility: hidden;
}
#sidenav-open:target {
visibility: visible;
}
}
Cuadrícula de CSS
Anteriormente, solo usaba diseños y componentes
de navegación lateral de posición absoluta o fija. Sin embargo, Grid, 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ú
<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
a fin de encontrar la relación que deseas para la superposición del menú y el botón de cierre con espacio negativo.
Transformaciones y transiciones 3D de CSS
Nuestro diseño ahora se apilan en un tamaño de viewport de dispositivo móvil. Hasta que agregue estilos nuevos, superará nuestro artículo de forma predeterminada. Estas son algunas de las UX que necesito en esta próxima sección:
- Cómo animar la apertura y el cierre
- Solo animar con movimiento si el usuario está de acuerdo con eso.
- Anima
visibility
para que el enfoque del teclado no ingrese al elemento fuera de pantalla
Cuando empiezo a implementar animaciones de movimiento, quiero tener en cuenta la accesibilidad como prioridad.
Movimiento accesible
No todo el mundo querrá una experiencia de movimiento de deslizamiento. En nuestra solución, esta preferencia se aplica mediante el ajuste de una variable de CSS --duration
dentro de una consulta de medios. Este valor de consulta de medios representa la preferencia del sistema operativo de un usuario para el movimiento (si está disponible).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Ahora, cuando nuestro panel de navegación lateral se abre y se cierra, si un usuario prefiere un movimiento reducido, muestro instantáneamente el elemento a la vista y mantengo el estado sin movimiento.
Transición, transformación y traducción
Navegación lateral fuera (predeterminado)
Para establecer el estado predeterminado de la navegación lateral en dispositivos móviles en un estado fuera de pantalla, posicionamos el elemento con transform: translateX(-110vw)
.
Ten en cuenta que agregué otro 10vw
al código típico fuera de pantalla de -100vw
para garantizar que el box-shadow
del panel de navegación lateral no se muestre en el viewport principal cuando esté oculto.
@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
coincida como :target
, establece la posición translateX()
en la base de inicio 0
y observa cómo CSS desliza el elemento desde su posición de salida (-110vw
) a su posición "dentro" de 0
sobre 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 esté fuera para que los sistemas no se enfoquen en un menú fuera de pantalla. Para lograrlo, configuro una transición de visibilidad cuando cambia :target
.
- Cuando entres, no hagas la transición de la visibilidad. Sé visible de inmediato para que pueda ver el elemento deslizarse y aceptar el enfoque.
- Cuando salgas, realiza la transición de la visibilidad, pero retrasala, de modo que cambie a
hidden
al final de la transición.
Mejoras en la UX de accesibilidad
Vínculos
Esta solución se basa en el cambio de la URL para que se administre el estado.
Como es de esperar, se debe usar el elemento <a>
aquí, y obtiene algunas funciones de accesibilidad de forma gratuita. Adornemos 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 para el mouse y el teclado.
:is(:hover, :focus)
Este práctico pseudoselector funcional de CSS nos permite ser inclusivos con rapidez con nuestros estilos de desplazamiento del cursor compartiéndolos también con enfoque.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Agrégale a JavaScript
Presiona escape
para cerrar
La tecla Escape
del teclado debería cerrar el menú a la derecha. Vamos a cablearlo.
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 apertura y cierre apila varias entradas en el historial del navegador, agrega el siguiente código JavaScript intercalado en el botón de cierre:
<a href="#" id="sidenav-close" title="Close Menu" aria-label="Close Menu" onchange="history.go(-1)"></a>
Esta acción quitará la entrada del historial de URL cuando se cierre, lo que hará que el menú nunca se haya abierto.
Enfoque en la UX
El siguiente fragmento nos ayuda a enfocarnos en los botones de apertura y cierre después de que se abren o cierran. Quiero que la activación o desactivación sea fácil.
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 el panel de navegación lateral, enfoca el botón de cierre. Cuando se cierre el panel de navegación lateral,
enfoca el botón Abrir. Para ello, llamo a focus()
en el elemento de JavaScript.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías? Esto hace que la arquitectura de componentes sea divertida. ¿Quién va a hacer la 1a versión con ranuras? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea una falla, twittea tu versión y la agregaré a la sección Remixes de la comunidad que se encuentra a continuación.
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