Cómo compilar un componente de desplazamiento multimedia

Una descripción general fundamental de cómo compilar un ScrollView horizontal responsivo para TVs, teléfonos, computadoras de escritorio, etc.

En esta publicación, quiero compartir mis ideas sobre cómo crear experiencias de desplazamiento horizontal para la Web que sean minimalistas, responsivas, accesibles y funcionen en todos los navegadores y plataformas (como las TVs). Prueba la demostración.

Demo

Si prefieres ver un video, aquí tienes una versión de esta publicación en YouTube:

Descripción general

Crearemos un diseño de desplazamiento horizontal destinado a alojar miniaturas de contenido multimedia o productos. El componente comienza como una lista <ul> humilde, pero se transforma con CSS en una experiencia de desplazamiento satisfactoria y fluida, que muestra imágenes y las ajusta a una cuadrícula. Se agrega JavaScript para facilitar las interacciones del índice itinerante, lo que ayuda a los usuarios del teclado a omitir la navegación por más de 100 elementos. Además, se usa una consulta de contenido multimedia experimental, prefers-reduced-data, para convertir el desplazamiento de contenido multimedia en una experiencia de desplazamiento de títulos liviana.

Comienza con un marcado accesible

Un control de desplazamiento de contenido multimedia está compuesto por solo un par de componentes principales, una lista con elementos. Una lista, en su forma más simple, puede viajar por todo el mundo y ser consumida de forma clara por todos. Un usuario que llega a esta página puede explorar una lista y hacer clic en un vínculo para ver un elemento. Esta es nuestra base accesible.

Publica una lista con un elemento <ul>:

<ul class="horizontal-media-scroller">
  <li></li>
  <li></li>
  <li></li>
  ...
<ul>

Haz que los elementos de la lista sean interactivos con un elemento <a>:

<li>
  <a href="#">
    ...
  </a>
</li>

Usa un elemento <figure> para representar semánticamente una imagen y su leyenda:

<figure>
  <picture>
    <img alt="..." loading="lazy" src="https://picsum.photos/500/500?1">
  </picture>
  <figcaption>Legends</figcaption>
</figure>

Observa los atributos alt y loading en <img>. El texto alternativo de un desplazamiento de contenido multimedia es una oportunidad de UX para ayudar a proporcionar contexto adicional a la miniatura, o como texto de resguardo si la imagen no se cargó, o bien proporciona una IU hablada para los usuarios que dependen de la tecnología de accesibilidad, como un lector de pantalla. Obtén más información con las cinco reglas de oro para el texto alternativo conforme.

El atributo loading acepta la palabra clave lazy como una forma de indicar que esta fuente de imagen se debe recuperar solo cuando la imagen está dentro del viewport. Esto puede ser muy útil para listas grandes, ya que los usuarios solo descargarán imágenes de los elementos que se muestran cuando se desplazan.

Cómo admitir la preferencia de esquema de colores del usuario

Usa color-scheme como una etiqueta <meta> para indicarle al navegador que tu página quiere los estilos de usuario-agente claros y oscuros proporcionados. Es un modo oscuro o claro gratuito, según cómo lo mires:

<meta name="color-scheme" content="dark light">

La metaetiqueta proporciona el indicador más temprano posible, de modo que el navegador pueda seleccionar un color de lienzo predeterminado oscuro si el usuario tiene una preferencia de tema oscuro. Esto significa que las navegaciones entre las páginas del sitio no mostrarán un fondo de lienzo blanco entre cargas. Tema oscuro sin interrupciones entre cargas, mucho más agradable a la vista.

Obtén mucho más información de Thomas Steiner en https://web.dev/color-scheme/.

Agregar contenido

Teniendo en cuenta la estructura de contenido anterior de ul > li > a > figure > picture > img, la siguiente tarea es agregar imágenes y títulos para desplazarse. La demo está repleta de imágenes y texto estáticos de marcadores de posición, pero no dudes en usarla desde tu fuente de datos favorita.

Cómo agregar estilo con CSS

Ahora es el momento de que el CSS tome esta lista genérica de contenido y la convierta en una experiencia. Netflix, las tiendas de aplicaciones y muchos más sitios y apps usan áreas de desplazamiento horizontales para empaquetar el viewport con categorías y opciones.

Crea el diseño del control deslizante

Es importante evitar cortar el contenido en los diseños o usar la truncación de texto con puntos suspensivos. Muchos televisores tienen controles de desplazamiento de contenido multimedia como este, pero a menudo recurren a la elipse del contenido. Este diseño no lo hace. También permite que el contenido multimedia anule el tamaño de la columna, lo que hace que 1 diseño sea lo suficientemente flexible como para manejar muchas combinaciones interesantes.

Se muestran 2 filas desplazables. Uno no tiene puntos suspensivos, lo que significa que es más alto y cada título es completamente legible. El otro es más corto y muchos títulos se cortan con puntos suspensivos.

El contenedor permite anular el tamaño de la columna proporcionando el tamaño predeterminado como una propiedad personalizada. Este diseño de cuadrícula tiene una opinión sobre el tamaño de las columnas, ya que solo administra el espaciado y la dirección:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2); /* parent owned value for children to be relative to*/
  margin: 0;
}

Luego, el elemento <picture> usa la propiedad personalizada para crear nuestra relación de aspecto base: un cuadro:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

Con solo algunos estilos menores, completa la estructura básica del control deslizante de contenido multimedia:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  & > li {
    display: inline-block; /* removes the list-item bullet */
  }

  & picture {
    inline-size: var(--size);
    block-size: var(--size);
  }
}

La configuración de overflow establece <ul> para permitir el desplazamiento y la navegación con el teclado por su lista. Luego, se quita el ::marker de cada elemento <li> secundario directo cuando se obtiene un nuevo tipo de visualización de inline-block.

Sin embargo, las imágenes aún no son responsivas y salen de los cuadros en los que se encuentran. Domésticalos con algunos tamaños, ajustes y estilos de borde, y un gradiente de fondo para cuando se carguen de forma diferida:

img {
  /* smash into whatever box it's in */
  inline-size: 100%;
  block-size: 100%;

  /* don't squish but do cover the space */
  object-fit: cover;

  /* soften the edges */
  border-radius: 1ex;
  overflow: hidden;

  /* if empty, show a gradient placeholder */
  background-image:
    linear-gradient(
      to bottom,
      hsl(0 0% 40%),
      hsl(0 0% 20%)
    );
}

Relleno de desplazamiento

La alineación con el contenido de la página, además de un área de superficie de desplazamiento de borde a borde, son fundamentales para un componente armonioso y minimalista.

Para lograr el diseño de desplazamiento de borde a borde que se alinea con nuestra tipografía y líneas de diseño, usa padding que coincida con scroll-padding:

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block: calc(var(--gap) / 2); /* make space for scrollbar and focus outline */
}

Se corrigió el error de padding del desplazamiento horizontal. Lo anterior muestra lo fácil que debería ser agregar padding a un contenedor de desplazamiento, pero hay problemas de compatibilidad pendientes con él (aunque se corrigieron en Chromium 91 y versiones posteriores). Consulta aquí para obtener un poco de historia, pero la versión breve es que el padding no siempre se tuvo en cuenta en una vista de desplazamiento.

Se destaca un cuadro en el lado final intercalado del último elemento de la lista, que muestra que el padding y el elemento tienen el mismo ancho para crear la alineación deseada.

Para engañar a los navegadores y que coloquen el padding al final del control de desplazamiento, apuntaré a la última figura de cada lista y adjuntaré un pseudoelemento que sea la cantidad de padding deseada.

.horizontal-media-scroller > li:last-of-type figure {
  position: relative;

  &::after {
    content: "";
    position: absolute;

    inline-size: var(--gap);
    block-size: 100%;

    inset-block-start: 0;
    inset-inline-end: calc(var(--gap) * -1);
  }
}

El uso de propiedades lógicas permite que el control deslizante de contenido multimedia funcione en cualquier modo de escritura y dirección del documento.

Ajuste de desplazamiento

Un contenedor de desplazamiento con desbordamiento puede convertirse en un viewport de ajuste con una línea de CSS. Luego, los elementos secundarios deben especificar cómo les gustaría alinearse con ese viewport.

.horizontal-media-scroller {
  --size: 150px;

  display: grid;
  grid-auto-flow: column;
  gap: calc(var(--gap) / 2);
  margin: 0;

  overflow-x: auto;
  overscroll-behavior-inline: contain;

  padding-inline: var(--gap);
  scroll-padding-inline: var(--gap);
  padding-block-end: calc(var(--gap) / 2);

  scroll-snap-type: inline mandatory;

  & figure {
    scroll-snap-align: start;
  }
}

Enfoque

La inspiración para este componente proviene de su gran popularidad en TVs, App Stores y mucho más. Muchas plataformas de videojuegos usan un control deslizante de contenido multimedia similar a este como diseño principal de la pantalla principal. El enfoque es un gran momento de UX aquí, no solo una pequeña adición. Imagina usar este control deslizante de contenido multimedia desde el sofá con un control remoto. Dale a esa interacción algunas mejoras menores:

.horizontal-media-scroller a {
  outline-offset: 12px;

  &:focus {
    outline-offset: 7px;
  }

  @media (prefers-reduced-motion: no-preference) {
    & {
      transition: outline-offset .25s ease;
    }
  }
}

Esto establece el estilo de contorno de enfoque 7px lejos del cuadro, lo que le brinda un espacio agradable. Si el usuario no tiene preferencias de movimiento para reducir el movimiento, se realiza la transición del desplazamiento, lo que le da un movimiento sutil al evento de enfoque.

Índice itinerante

Los usuarios de gamepad y teclado necesitan atención especial en estas listas largas de opciones y contenido de desplazamiento. El patrón común para resolver este problema se denomina índice itinerante. Ocurre cuando un contenedor de elementos está enfocado con el teclado, pero solo se permite que 1 elemento secundario mantenga el enfoque a la vez. Esta experiencia de un solo elemento enfocable a la vez está diseñada para permitir que se omita la lista potencialmente larga de elementos, en lugar de presionar Tab más de 50 veces para llegar al final.

Hay 300 elementos en ese primer control deslizante de la demostración. Podemos hacer algo mejor que hacer que recorra todas para llegar a la siguiente sección.

Para crear esta experiencia, JavaScript debe observar los eventos del teclado y los eventos de enfoque. Creé una pequeña biblioteca de código abierto en npm para ayudar a que esta experiencia del usuario sea fácil de lograr. A continuación, te indicamos cómo usarlo para los 3 controles de desplazamiento:

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Esta demostración consulta el documento en busca de los controles de desplazamiento y, para cada uno de ellos, llama a la función rovingIndex(). Pasa el elemento rovingIndex() para obtener la experiencia de desplazamiento, como un contenedor de lista, y un selector de búsqueda de destino, en caso de que los destinos de enfoque no sean descendientes directos.

document.querySelectorAll('.horizontal-media-scroller')
  .forEach(scroller =>
    rovingIndex({
      element: scroller,
      target: 'a',
}))

Para obtener más información sobre este efecto, consulta la biblioteca de código abierto roving-ux.

Aspect-ratio

En el momento de escribir esta publicación, la compatibilidad con aspect-ratio está detrás de una marca en Firefox, pero está disponible en navegadores Chromium o decodificadores de TV. Dado que el diseño de la cuadrícula del control deslizante de contenido multimedia solo especifica la dirección y el espaciado, el tamaño puede cambiar dentro de una consulta de contenido multimedia que verifica la compatibilidad con la relación de aspecto. Mejora progresiva en algunos controles de desplazamiento de contenido multimedia más dinámicos.

Se muestra un cuadro con una relación de aspecto de 4:4 junto a las otras relaciones de diseño utilizadas de 16:9 y 4:3.

@supports (aspect-ratio: 1) {
  .horizontal-media-scroller figure > picture {
    inline-size: auto; /* for a block-size driven ratio */
    aspect-ratio: 1; /* boxes by default */

    @nest section:nth-child(2) & {
      aspect-ratio: 16/9;
    }

    @nest section:nth-child(3) & {
      /* double the size of the others */
      block-size: calc(var(--size) * 2);
      aspect-ratio: 4/3;

      /* adjust size to fit more items into the viewport */
      @media (width <= 480px) {
        block-size: calc(var(--size) * 1.5);
      }
    }
  }
}

Si el navegador admite la sintaxis aspect-ratio, las imágenes del control deslizante de contenido multimedia se actualizan al tamaño aspect-ratio. Con la sintaxis de anidación de borradores, cada imagen cambia su relación de aspecto según si es la primera, la segunda o la tercera fila. La sintaxis anidada también permite establecer algunos pequeños ajustes de la ventana de visualización, junto con la otra lógica de tamaño.

Con ese CSS, como la función está disponible en más motores de navegador, se renderizará un diseño fácil de administrar, pero más atractivo a la vista.

Prefiere datos reducidos

Si bien la siguiente técnica solo está disponible detrás de una marca en Canary, quería compartir cómo pude ahorrar una cantidad considerable de tiempo de carga de la página y de uso de datos con algunas líneas de CSS. La consulta multimedia prefers-reduced-data del nivel 5 permite preguntar si el dispositivo se encuentra en algún estado de datos reducidos, como un modo de ahorro de datos. Si es así, puedo modificar el documento y, en este caso, ocultar las imágenes.

ALT_TEXT_HERE

figure {
  @media (prefers-reduced-data: reduce) {
    & {
      min-inline-size: var(--size);

      & > picture {
        display: none;
      }
    }
  }
}

Se puede navegar por el contenido, pero sin el costo de descargar las imágenes pesadas. Este es el sitio antes de agregar el CSS de prefers-reduced-data:

(7 solicitudes, 100 KB de recursos en 131 ms)

ALT_TEXT_HERE

Este es el rendimiento del sitio después de agregar el CSS prefers-reduced-data:

ALT_TEXT_HERE

(71 solicitudes, 1.2 MB de recursos en 1.07 s)

64 solicitudes menos, que serían las ~60 imágenes dentro del viewport (pruebas realizadas en una pantalla panorámica) de esta pestaña del navegador, un aumento de la carga de la página de alrededor del 80% y el 10% de los datos a través de la red. CSS bastante potente.

Conclusión

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 una cuenta de Codepen o aloja tu propia demo, envíame un tweet con ella y la agregaré a la sección de remixes de la comunidad a continuación.

Origen

Remixes de la comunidad

Aún no hay nada que ver.