Descripción general fundamental de cómo crear una diapositiva responsiva fuera de la barra de navegación lateral
En esta publicación, quiero compartir contigo el prototipo de un componente de Sidenav para la Web que es responsivo, tiene estado, admite la navegación con teclado, funciona con y sin JavaScript y 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 una tarea difícil crear un sistema de navegación adaptable. Algunos usuarios usarán un teclado, otros tendrán computadoras de escritorio potentes y otros lo visitarán desde un dispositivo móvil pequeño. Todas las personas que visiten el sitio deben poder abrir y cerrar el menú.
Tácticas web
En esta exploración de componentes, tuve la dicha de combinar algunas funciones esenciales 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
- Mejoras de la UX de JS para
focus
Mi solución tiene una barra lateral y se activa o desactiva solo cuando se encuentra en un viewport de "dispositivos móviles" 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 para computadoras de escritorio estático.
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
que debe coincidir 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, se cambia el estado de hash de la URL de nuestra página y, luego, con una seudoclase, muestro y oculta la barra de 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 de 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ú
<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 deseas para la superposición del menú y el botón para cerrar el espacio negativo.
Transformaciones y transiciones CSS 3D
Ahora, nuestro diseño se apila en un tamaño de viewport para dispositivos móviles. Hasta que agregué nuevos estilos, se superpone a nuestro artículo de forma predeterminada. Estas son algunas UX que estoy buscando en la próxima sección:
- Cómo animar la apertura y el cierre
- Animar solo con movimiento si el usuario está de acuerdo con eso
- Se anima
visibility
para que el enfoque del teclado no ingrese al elemento fuera de la pantalla
Cuando empiezo a implementar animaciones de movimiento, quiero empezar con la accesibilidad como prioridad.
Movimiento accesible
No todos querrán una experiencia de deslizamiento hacia afuera. 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 de movimiento del sistema operativo de un usuario (si está disponible).
#sidenav-open {
--duration: .6s;
}
@media (prefers-reduced-motion: reduce) {
#sidenav-open {
--duration: 1ms;
}
}
Ahora, cuando nuestra barra de navegación lateral se abre y se cierra, si un usuario prefiere un movimiento reducido, muevo inmediatamente el elemento a la vista, manteniendo el estado sin movimiento.
Transición, transformación y traducción
Navegación lateral hacia afuera (predeterminado)
Para establecer el estado predeterminado de nuestra navegación lateral en dispositivos móviles en un estado fuera de la pantalla, coloco el elemento con transform: translateX(-110vw)
.
Ten en cuenta que se agregó otro 10vw
al código típico fuera de pantalla de -100vw
para garantizar que el box-shadow
de la barra de navegación lateral no se vea 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
coincide como :target
, establece la posición translateX()
en la base de referencia 0
y observa cómo CSS desliza el elemento desde su posición fuera de -110vw
hasta su posición "dentro" de 0
sobre var(--duration)
cuando 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 de la pantalla para que los sistemas no coloquen el enfoque en un menú fuera de pantalla. Para ello, configuro una transición de visibilidad cuando cambie :target
.
- Cuando entres, no hagas una transición de la visibilidad; sé visible de inmediato para que pueda ver el elemento deslizarse y aceptar el enfoque.
- Cuando sales, cambia la visibilidad, pero la retrasa, por lo que cambia a
hidden
al final de la transición.
Mejoras en la UX de accesibilidad
Vínculos
Esta solución se basa en cambiar la URL para que se administre el estado.
Naturalmente, aquí se debería usar el elemento <a>
, y se obtienen algunas funciones de accesibilidad útiles de forma gratuita. Adornaremos nuestros elementos interactivos con etiquetas que indiquen 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 con rapidez con nuestros estilos de desplazamiento compartiéndolos con el foco.
.hamburger:is(:hover, :focus) svg > line {
stroke: hsl(var(--brandHSL));
}
Amplificación en JavaScript
Presiona escape
para cerrar
¿La tecla Escape
del teclado debería cerrar el menú correctamente? Vamos a conectar 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 apertura y cierre apila 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>
Esta acción quitará la entrada del historial de URLs cuando se cierre, lo que lo hará como si el menú nunca se hubiera abierto.
Enfoque en la UX
El siguiente fragmento nos ayuda a enfocarnos en los botones de abrir y cerrar después de que se abren o se cierran. Quiero que activar o desactivar 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 la barra de navegación lateral, enfoca el botón de cierre. Cuando se cierre la barra de navegación lateral,
enfoca el botón para 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 creará la primera versión con ranuras? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crea un Glitch, twittea tu versión y la agregaré a la sección Remixes de la comunidad 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