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 YouTube de esta publicación:

Descripción general

Compilaremos 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 lenguaje de 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 doradas para el texto alternativo compatible.

El atributo loading acepta la palabra clave lazy como una forma de indicar que esta fuente de imagen solo se debe recuperar 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 la señal más temprana posible para que el navegador pueda seleccionar un color de lienzo oscuro predeterminado si el usuario prefiere el 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

Dada 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. 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 se basa en el tamaño de la columna y 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 unos pocos estilos más pequeños, completa los elementos básicos de la barra de desplazamiento 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 configura <ul> para permitir el desplazamiento y la navegación con el teclado en su lista. Luego, se quitará el ::marker de cada elemento secundario directo <li> al obtener un nuevo tipo de pantalla 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 crear un componente armonioso y mínimo.

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 la barra de desplazamiento 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, corresponde a los elementos secundarios 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 desplazable. 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. Este único elemento enfocable a la vez está diseñado para permitir la omisión de la lista de elementos potencialmente larga, en lugar de presionar la tecla Tab más de 50 veces para llegar al final.

Hay 300 elementos en la primera barra de desplazamiento de la demostración. Podemos hacerlo mejor que hacer que los atraviesen a todos 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 de desplazamiento de contenido multimedia solo especifica la dirección y el espaciado, el tamaño puede cambiar dentro de una consulta de medios que comprueba 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 la reducción de datos

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 desarrollar 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.