Descripción general básica sobre cómo compilar una vista de desplazamiento horizontal adaptable para TVs, teléfonos, computadoras de escritorio, etcétera
En esta entrada, quiero compartir ideas sobre cómo crear experiencias de desplazamiento horizontal para la Web que sean minimalistas, responsivas, accesibles y que funcionen en todos los navegadores y plataformas (¡como las TVs!). Prueba la demostración.
Si prefieres un video, aquí tienes una versión de este artículo en YouTube:
Descripción general
Compilaremos un diseño de desplazamiento horizontal destinado a alojar miniaturas de contenido multimedia o productos. El componente comienza como una humilde lista de <ul>
, 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 agregó JavaScript para facilitar las interacciones con el índice itinerante, lo que ayuda a los usuarios del teclado a omitir el recorrido de más de 100 elementos.
Además, se usa una consulta de medios experimental, prefers-reduced-data
, para convertir el desplazador de medios en una experiencia de desplazador de títulos liviana.
Comienza con un lenguaje de marcado accesible
Un desplazador de medios se compone de 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 claramente 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.
Entrega 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 carrusel de medios es una oportunidad de UX para ayudar a brindar contexto adicional a la miniatura o como texto de resguardo si no se cargó la imagen, o bien proporciona una IU hablada para los usuarios que dependen de tecnología de asistencia, como un lector de pantalla. Obtén más información con las cinco reglas de oro para el texto alternativo que cumple con los requisitos.
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 desplacen a la vista.
Cómo admitir la preferencia de esquema de color del usuario
Usa color-scheme
como una etiqueta <meta>
para indicarle al navegador que tu página requiere los estilos de agente de usuario proporcionados tanto para el modo claro como para el modo oscuro. Es un modo oscuro o claro gratuito, según cómo lo mires:
<meta name="color-scheme" content="dark light">
La etiqueta meta proporciona el primer indicador posible, por lo que el navegador puede seleccionar un color de lienzo predeterminado oscuro si el usuario tiene una preferencia de tema oscuro. Esto significa que la navegación entre las páginas del sitio no mostrará un fondo blanco entre las cargas. El tema oscuro se aplica sin problemas entre cargas, lo que es mucho más agradable para la vista.
Obtén mucha 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. Incluí imágenes y texto de marcador de posición estáticos en la demostración, pero puedes usar tu fuente de datos favorita.
Agrega un estilo con CSS
Ahora es el momento de que CSS tome esta lista genérica de contenido y la convierta en una experiencia. Netflix, las tiendas de aplicaciones y muchos otros sitios y apps usan áreas de desplazamiento horizontal para llenar el viewport con categorías y opciones.
Cómo crear el diseño del desplazador
Es importante evitar cortar el contenido en los diseños o depender del truncamiento de texto con puntos suspensivos. Muchas televisiones tienen barras de desplazamiento de medios como esta, pero con demasiada frecuencia recurren a la elipsis de 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 controlar 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: una caja:
.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 más, completa la estructura básica del control deslizante de medios:
.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);
}
}
Establecer overflow
configura <ul>
para permitir el desplazamiento y la navegación con el teclado a través de su lista. Luego, se quita el ::marker
de cada elemento <li>
secundario directo obteniendo un nuevo tipo de visualización de inline-block
.
Sin embargo, las imágenes aún no son responsivas y sobresalen de las cajas en las que se encuentran. Domínalos 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 y un área de superficie de desplazamiento de borde a borde son fundamentales para lograr 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 */
}
Corrección de errores de padding de desplazamiento horizontal En la imagen anterior, se 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í un poco de historia, pero la versión corta 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 elemento de desplazamiento, segmentaré la última figura de cada lista y agregaré un seudoelemento que tenga 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 medios funcione en cualquier modo de escritura y dirección del documento.
Ajuste de desplazamiento
Un contenedor de desplazamiento con desbordamiento puede convertirse en una ventana gráfica de ajuste con una línea de CSS. Luego, los elementos secundarios deben especificar cómo desean alinearse con esa ventana gráfica.
.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 medios muy similar a este como diseño principal de la pantalla de inicio. El enfoque es un momento de UX muy importante aquí, no solo una pequeña adición. Imagina que usas este control deslizante de medios desde tu sofá con un control remoto. Dale a esa interacción algunas pequeñas mejoras:
.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 del contorno de enfoque 7px
lejos de la caja, lo que le da un espacio agradable. Si el usuario no tiene preferencias de movimiento para reducirlo, se realiza la transición del desplazamiento, lo que le da un movimiento sutil al evento de enfoque.
Índice de itinerancia
Los usuarios de teclados y gamepads necesitan atención especial en estas largas listas de contenido y opciones desplazables. El patrón común para resolver este problema se denomina índice itinerante. Se produce cuando un contenedor de elementos está enfocado con el teclado, pero solo se permite que un elemento secundario mantenga el enfoque a la vez. Esta experiencia de un solo elemento enfocable a la vez está diseñada para permitir omitir 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 el primer panel de desplazamiento de la demostración. Podemos hacer algo mejor que obligarlos a recorrerlos todos para llegar a la siguiente sección.
Para crear esta experiencia, JavaScript debe observar los eventos de 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 desplazadores:
import {rovingIndex} from 'roving-ux';
rovingIndex({
element: someElement
});
En esta demostración, se consulta el documento para obtener los elementos de desplazamiento y, para cada uno de ellos, se llama a la función rovingIndex()
. Pasa el rovingIndex()
del elemento para obtener la experiencia de exploración, como un contenedor de lista, y un selector de consultas 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.
Relación de aspecto
Al momento de escribir esta entrada, la compatibilidad con aspect-ratio
está detrás de una marca en Firefox, pero está disponible en los navegadores Chromium o en los decodificadores. Dado que el diseño de cuadrícula del desplazador de medios solo especifica la dirección y el espaciado, el tamaño puede cambiar dentro de una consulta de medios que verifique la compatibilidad con la relación de aspecto.
Mejora progresiva en algunos controles deslizantes de medios 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 de aspect-ratio
, las imágenes del desplazador de medios se actualizan al tamaño de aspect-ratio
. Con la sintaxis de anidación de borradores, cada imagen cambia su relación de aspecto según si se encuentra en la primera, segunda o tercera fila. La sintaxis de anidación también permite establecer algunos ajustes pequeños del viewport, junto con la otra lógica de tamaño.
Con ese CSS, a medida que la función esté disponible en más motores de navegador, se renderizará un diseño fácil de administrar y más atractivo visualmente.
Prefiere datos reducidos
Si bien esta próxima 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 uso de datos con unas pocas líneas de CSS. La consulta de medios 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;
}
}
}
}
El contenido sigue siendo navegable, 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 aproximadamente 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 aproximadamente un 80% y el 10% de los datos a través del cable. CSS bastante potente.
Conclusión
Ahora que sabes cómo lo hice, ¿cómo lo harías tú? 🙂
Diversifiquemos nuestros enfoques y aprendamos todas las formas de crear contenido en la Web. Crea un Codepen o aloja tu propia demostración, envíame un tuit con ella y la agregaré a la sección de remixes de la comunidad que se encuentra a continuación.
Origen
Remixes de la comunidad
Aún no hay nada para ver aquí.