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.
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.
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.
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.
@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.
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)
Este es el rendimiento del sitio después de agregar el CSS prefers-reduced-data
:
(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.