Cómo crear un componente Historias

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 mis ideas sobre la compilación de un componente de Historias para la Web que sea responsivo, admita la navegación con el teclado y funcione en todos los navegadores.

Demo

Si prefieres una demostración práctica de cómo compilar este componente de Historias por tu cuenta, consulta el codelab del componente de 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 Historias son las 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 la presión solo para dispositivos móviles para navegar por varias suscripciones. Por ejemplo, en Instagram, los usuarios abren la historia de un amigo y ven las fotos. Por lo general, hacen muchos amigos a la vez. Si un usuario presiona el lado derecho del dispositivo, se avanza a la siguiente historia de ese amigo. Si desliza el dedo hacia la derecha, el usuario pasa a otro amigo. Un componente de historia es bastante similar a un carrusel, pero permite navegar por un array multidimensional en lugar de uno de una sola dimensión. Es como si hubiera un carrusel dentro de cada carrusel. 🤯

Matriz multidimensional visualizada con tarjetas. De izquierda a derecha, hay una pila de tarjetas con bordes morados y, dentro de cada tarjeta, hay 1 o varias tarjetas con bordes cian. Lista en una lista.
1er carrusel de amigos
2º carrusel "apilado" de historias
👍 Lista en una lista, también conocida como un array multi-dimensional

Elige las herramientas adecuadas para el trabajo

En general, encontré que este componente es bastante sencillo de compilar, gracias a algunas funciones críticas de la plataforma web. Analicemos cada una de ellas.

Cuadrícula de CSS

Nuestro diseño no fue un gran desafío para CSS Grid, ya que está equipado con algunas formas potentes de controlar el contenido.

Diseño de Amigos

Nuestro wrapper de componentes .stories principal es un scrollview 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;
}
Usar el modo de dispositivo de las Herramientas para desarrolladores de Chrome para destacar las columnas creadas por la cuadrícula

Desglosemos ese diseño grid:

  • Rellenamos de forma explícita el viewport en dispositivos móviles con 100vh y 100vw, y restringimos el tamaño en computadoras de escritorio
  • / separa nuestras plantillas de filas y columnas.
  • auto-flow se traduce como grid-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, imagina que el tamaño de la fila es la altura del viewport y cada columna es el ancho del viewport. Siguiendo con el ejemplo de las Historias de Snapchat y las Historias de Instagram, cada columna será la historia de un amigo. Queremos que las historias de amigos continúen fuera de viewport, por lo que tenemos un lugar hasta el cual desplazarnos. La cuadrícula creará tantas columnas como necesite para diseñar tu código HTML para cada historia de amigo, lo que creará un contenedor de desplazamiento dinámico y responsivo. La cuadrícula nos permitió centralizar todo el efecto.

Apilamiento

Para cada amigo, necesitamos que sus historias estén en un estado listo para la paginación. En preparación para la animación y otros patrones divertidos, elegí una pila. Cuando hablo de “apilado”, me refiero a que miras hacia abajo un sándwich, no 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 columnas comparten un alias ([story]) y, luego, cada elemento secundario se asigna a ese espacio de una sola celda con suavizado:

.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.story {
  grid-area: story;
  background-size: cover;
  
}

De esta manera, nuestro HTML controla el orden de apilado y también mantiene todos los elementos en flujo. Observa que no tuvimos que hacer nada con el posicionamiento absolute ni z-index, ni tampoco tuvimos que corregir el cuadro con height: 100% o width: 100%. La cuadrícula superior ya definió el tamaño de la vista de la imagen de la historia, por lo que no se necesitó decirle a ninguno de estos componentes de la historia que la ocupe.

Puntos de ajuste de desplazamiento de CSS

La especificación de puntos de ajuste de desplazamiento de CSS facilita el bloqueo de elementos en el viewport durante el desplazamiento. Antes de que existieran estas propiedades CSS, debías usar JavaScript, y era… complicado, por decir lo menos. Consulta Introducing CSS Scroll Snap Points de Sarah Drasner para obtener un excelente desglose de cómo usarlos.

Desplazamiento horizontal sin estilos scroll-snap-points y con ellos. Sin ella, los usuarios pueden desplazarse libremente como de costumbre. Con él, el navegador se apoya suavemente en cada elemento.
elemento superior
.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 desplazamiento horizontal define el comportamiento de ajuste.
niño
.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
Los niños aceptan ser un objetivo de Snap.

Elegí los puntos de ajuste de desplazamiento por varios motivos:

  • Accesibilidad gratuita. En la especificación de los puntos de ajuste de desplazamiento, se establece que cuando se presionan las teclas de flecha izquierda y flecha derecha se deben mover los puntos de ajuste de forma predeterminada.
  • Especificación en crecimiento: La especificación de puntos de ajuste de desplazamiento recibe funciones y mejoras nuevas todo el tiempo, lo que significa que mi componente de Historias probablemente solo mejorará a partir de ahora.
  • Facilidad de implementación. Los puntos de ajuste de desplazamiento se compilan prácticamente para el caso de uso de paginación horizontal centrado en la función táctil.
  • Inercia libre del estilo de la plataforma. Todas las plataformas se desplazarán y descansarán en su estilo, a diferencia de la inercia normalizada, que puede tener un estilo de desplazamiento y reposo sorprendentes.

Compatibilidad entre navegadores

Realizamos pruebas en Opera, Firefox, Safari y Chrome, además de Android y iOS. Este es un breve resumen de las funciones web en las que encontramos diferencias en las capacidades y la compatibilidad.

Sin embargo, algunos CSS no se aplicaron, por lo que algunas plataformas no cuentan con optimizaciones de UX. Disfruté no tener que administrar estas funciones y confié en que 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 tiene que detenerse en cada imagen después de que un usuario interactúa con él. Puede que esté bien o que se recomiende recorrer rápidamente el carrusel. Por otro lado, las historias se navegan mejor de a una, y eso es exactamente lo que proporciona scroll-snap-stop.

.user {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

En el momento de escribir esta publicación, scroll-snap-stop solo es compatible con los navegadores basados en Chromium. Consulta Compatibilidad con navegadores para obtener actualizaciones. Sin embargo, no es un bloqueador. Solo significa que, en los navegadores no compatibles, los usuarios pueden omitir un amigo por accidente. Por lo tanto, los usuarios deberán tener más cuidado, o bien tendremos que escribir JavaScript para asegurarnos de que un amigo o amiga omitido no se marque como visto.

Obtén más información en las especificaciones si te interesa.

overscroll-behavior

¿Alguna vez te desplazaste por un diálogo modal y, de repente, comenzaste a desplazar el contenido detrás del diálogo? overscroll-behavior le permite al desarrollador atrapar ese desplazamiento y nunca dejarlo. Es ideal para todo tipo de ocasiones. El componente Mis historias 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 fueron los 2 navegadores que no admitían esta función, y eso está bien. Esos usuarios tendrán una experiencia de sobredesplazamiento como están acostumbrados y es posible que nunca noten esta mejora. Personalmente, soy un gran fan y me gusta incluirlo como parte de casi todas las funciones de desplazamiento horizontal que implemento. Es una adición inofensiva que solo puede mejorar la UX.

scrollIntoView({behavior: 'smooth'})

Cuando un usuario toca 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 próximo amigo y solicitar que se desplace para verlo. La compatibilidad con los conceptos básicos es excelente; todos los navegadores lo desplazaron hasta que estuvo a la vista. Sin embargo, no todos los navegadores lo hicieron 'smooth'. Esto solo significa que se desplazó hasta la vista en lugar de ajustarse.

element.scrollIntoView({
  behavior: 'smooth'
})

Safari era el único navegador que no admitía behavior: 'smooth' aquí. Consulta Compatibilidad con navegadores para obtener actualizaciones.

Práctica

Ahora que sabes cómo lo hice, ¿cómo lo harías? Diversifiquemos nuestros enfoques y aprendamos todas las formas de compilar en la Web. Crea un Glitch, tuitea tu versión y la agregaré a la sección Remixes de la comunidad a continuación.

Remixes de la comunidad