Una descripción general fundamental de cómo crear una experiencia similar a las historias de Instagram en la Web
En esta publicación, quiero compartir las ideas sobre cómo crear un componente de Historias para la Web que sea responsivo, sea compatible con la navegación con el teclado y funcione en diferentes navegadores.
Si prefieres una demostración práctica para compilar este componente de Historias por tu cuenta, consulta el codelab del componente Historias.
Si prefieres ver un video, aquí tienes una versión de YouTube de esta publicación:
Descripción general
Dos ejemplos populares de la UX de las historias son las historias de Snapchat y las de Instagram (sin mencionar las flotas). En términos generales de UX, las historias suelen ser un patrón centrado en el toque y solo en dispositivos móviles para navegar por varias suscripciones. Por ejemplo, en Instagram, los usuarios abren la historia de un amigo y revisan las fotos que aparecen en ella. Suelen hacer esto con muchos amigos a la vez. Cuando un usuario presiona el lado derecho del dispositivo, avanza a la siguiente historia de ese amigo. Al deslizar el dedo hacia la derecha, un usuario se pasa a otro amigo. Un componente de historia es bastante similar a un carrusel, pero permite navegar por un array multidimensional en lugar de un array de una sola dimensión. Es como si hubiera un carrusel dentro de cada carrusel. 🤯
![Se visualizó un array multidimensional con tarjetas. De izquierda a derecha, hay una pila de tarjetas con bordes púrpuras y, dentro de cada tarjeta, hay una a muchas tarjetas con bordes turquesa. Lista en una lista.](https://web.dev/static/articles/building/a-stories-component/image/visualized-multi-dimensio-bca9c1e40444a.png?authuser=6&hl=es)
2o carrusel de historias "apilados"
👍 Lista en una lista, también conocida como array multidimensional
Elegir las herramientas adecuadas para el trabajo
En general, este componente me pareció bastante fácil de compilar, gracias a algunas funciones esenciales de la plataforma web. Veamos cuáles son.
Cuadrícula de CSS
Nuestro diseño no es muy exigente para la cuadrícula de CSS, ya que está equipado con algunas formas poderosas de manipular el contenido.
Diseño de amigos
Nuestro wrapper principal de componente .stories
es una vista de desplazamiento horizontal que prioriza los dispositivos móviles:
.stories {
inline-size: 100vw;
block-size: 100vh;
display: grid;
grid: 1fr / auto-flow 100%;
gap: 1ch;
overflow-x: auto;
scroll-snap-type: x mandatory;
overscroll-behavior: contain;
touch-action: pan-x;
}
/* desktop constraint */
@media (hover: hover) and (min-width: 480px) {
max-inline-size: 480px;
max-block-size: 848px;
}
Desglosemos ese diseño de grid
:
- Completamos el viewport de forma explícita en dispositivos móviles con
100vh
y100vw
, y limitamos el tamaño en computadoras de escritorio /
separa nuestras plantillas de filas y columnasauto-flow
se traduce comogrid-auto-flow: column
- La plantilla de flujo automático es
100%
, que en este caso es el ancho de la ventana de desplazamiento.
En un teléfono celular, piensa que el tamaño de la fila corresponde a la altura del viewport y cada columna es el ancho del viewport. Siguiendo con los ejemplos de Historias de Snapchat e Historias de Instagram, cada columna será la historia de un amigo. Queremos que las historias de amigos continúen fuera del viewport, por lo que tenemos un lugar al que desplazarse. La cuadrícula creará todas las columnas que necesite para diseñar tu HTML para cada historia de un amigo, lo que creará un contenedor de desplazamiento dinámico y responsivo para nosotros. Grid nos permitió centralizar todo el efecto.
Apilado
Para cada amigo, necesitamos sus historias en un estado listo para la paginación. Como preparación para la animación y otros patrones divertidos, elegí una pila. Cuando digo “apilar”, es como mirar un sándwich hacia abajo, no como si miraras desde un lado.
Con la cuadrícula de CSS, podemos definir una cuadrícula de una sola celda (es decir, un cuadrado), en la que las filas y las columnas comparten un alias ([story]
) y, luego, cada elemento secundario se asigna a ese espacio de celda única con alias:
.user {
display: grid;
grid: [story] 1fr / [story] 1fr;
scroll-snap-align: start;
scroll-snap-stop: always;
}
.story {
grid-area: story;
background-size: cover;
…
}
Esto permite que nuestro código HTML controle el orden de apilado y mantiene todos los elementos en flujo. Observa que no tuvimos que hacer nada con el posicionamiento de absolute
o de z-index
, y no tuvimos que corregir con height: 100%
o width: 100%
. La cuadrícula superior ya definió el tamaño del viewport de imágenes de la historia, por lo que no es necesario contar estos componentes de la historia para llenarlo.
Puntos de ajuste de desplazamiento de CSS
La especificación de los puntos de ajuste de desplazamiento de CSS facilita el bloqueo de elementos en el viewport durante el desplazamiento. Antes de que estas propiedades de CSS existieran, tenías que usar JavaScript, y era... difícil, por decirlo menos. Consulta Presentamos los puntos de ajuste del desplazamiento de CSS de Sarah Drasner para obtener un desglose detallado de cómo usarlos.
scroll-snap-points
.
Sin él, los usuarios pueden desplazarse libremente con normalidad. Con él, el navegador se apoya suavemente sobre cada elemento.
.stories { display: grid; grid: 1fr / auto-flow 100%; gap: 1ch; overflow-x: auto; scroll-snap-type: x mandatory; overscroll-behavior: contain; touch-action: pan-x; }El elemento superior con sobredesplazamiento define el comportamiento de ajuste.
.user { display: grid; grid: [story] 1fr / [story] 1fr; scroll-snap-align: start; scroll-snap-stop: always; }Los niños aceptan ser objetivos rápidos.
Elegí los Puntos de ajuste de desplazamiento por varias razones:
- Accesibilidad gratuita La especificación de los puntos de ajuste de desplazamiento establece que, cuando se presionan las teclas Flecha izquierda y Flecha derecha, se debería mover por los puntos de ajuste de forma predeterminada.
- Una especificación que va en aumento: La especificación de Scroll Snap Points recibe nuevas funciones y mejoras todo el tiempo, lo que significa que el componente Historias probablemente mejorará de aquí en adelante.
- Facilidad de implementación. Los puntos de ajuste de desplazamiento se compilan en la práctica para el caso de uso de paginación horizontal centrado en los toques.
- Inercia de estilo de plataforma libre. Cada plataforma se desplazará y descansará según su estilo, a diferencia de la inercia normalizada, que puede tener un extraño estilo de desplazamiento y reposo.
Compatibilidad entre navegadores
Lo probamos en Opera, Firefox, Safari y Chrome, y en iOS y Android. Este es un breve resumen de las funciones web en las que encontramos diferencias en cuanto a las capacidades y la compatibilidad.
Sin embargo, teníamos algunos CSS que no se aplicaron, por lo que, actualmente, algunas plataformas están perdiendo optimizaciones de UX. Disfruté no tener que administrar estas funciones y estoy seguro de que finalmente llegarán a otros navegadores y plataformas.
scroll-snap-stop
Los carruseles fueron uno de los principales casos de uso de UX que impulsaron la creación de la especificación de puntos de ajuste de desplazamiento de CSS. A diferencia de las historias, un carrusel no siempre necesita detenerse en cada imagen después de que un usuario interactúa con ella. Puede ser útil o alentado a que
recorras el carrusel rápidamente. Por otro lado, es mejor navegar una por una las historias, y eso es exactamente lo que proporciona scroll-snap-stop
.
.user {
scroll-snap-align: start;
scroll-snap-stop: always;
}
Al momento de redactar esta publicación, scroll-snap-stop
solo es compatible con los navegadores
basados en Chromium. Consulta la compatibilidad de los navegadores para conocer las actualizaciones. Sin embargo, no es un obstáculo. Solo significa que, en navegadores no compatibles, los usuarios pueden omitir a un amigo por accidente. Por lo tanto, los usuarios solo deberán ser más cuidadosos, o tendremos que escribir JavaScript para garantizar que un amigo omitido no se marque como visto.
Si te interesa, puedes obtener más información en la especificación.
overscroll-behavior
¿Alguna vez te desplazaste por una ventana modal cuando, de repente, empezaste a desplazar el contenido detrás de ella?
overscroll-behavior
permite que el desarrollador intercepte ese desplazamiento y nunca lo deje. Son perfectos para todo tipo de ocasiones. El componente de My Stories lo usa para evitar que los gestos de deslizamiento y desplazamiento adicionales salgan del componente.
.stories {
overflow-x: auto;
overscroll-behavior: contain;
}
Safari y Opera eran los 2 navegadores que no admitían esto, y está bien. Esos usuarios obtendrán una experiencia de sobredesplazamiento como están acostumbrados y es posible que nunca noten esta mejora. Personalmente, soy un gran fanático y me gusta incluirlo como parte de casi todas las funciones de sobredesplazamiento que implementé. Es una adición inocua que solo puede generar una UX mejorada.
scrollIntoView({behavior: 'smooth'})
Cuando un usuario presiona o hace clic, y llega al final del conjunto de historias de un amigo, es hora de pasar al siguiente amigo en el conjunto de puntos de ajuste de desplazamiento. Con JavaScript, pudimos hacer referencia al siguiente amigo y solicitar que se desplazara hasta que se vea. La compatibilidad con los aspectos básicos de esto es excelente: todos los navegadores lo desplazaban hasta que quedó visible. Sin embargo, no todos los navegadores lo hicieron 'smooth'
. Esto solo significa que se desplaza hasta la vista, en lugar de ajustarse.
element.scrollIntoView({
behavior: 'smooth'
})
Safari fue el único navegador que no admitía behavior: 'smooth'
aquí. Consulta la compatibilidad de los navegadores para conocer las actualizaciones.
Actividades prácticas
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. 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
- @geoffrich_ con Svelte: demostración y código
- @GauteMeekOlsen con Vue: demostración y código
- @AnaestheticsApp con Lit: demo y código