Créer un composant de défilement multimédia

Présentation de base sur la création d'une vue de défilement horizontale responsive pour les téléviseurs, les téléphones, les ordinateurs de bureau, etc.

Dans cet article, je souhaite partager des réflexions sur la façon de créer des expériences de défilement horizontal pour le Web qui sont minimalistes, responsives, accessibles et compatibles avec tous les navigateurs et plates-formes (comme les téléviseurs). Essayez la démo.

Démo

Si vous préférez regarder une vidéo, voici une version YouTube de cet article :

Présentation

Nous allons créer une mise en page à défilement horizontal destinée à héberger des miniatures de contenus multimédias ou de produits. Le composant commence comme une simple liste <ul>, mais est transformé en une expérience de défilement satisfaisante et fluide à l'aide du CSS, en affichant des images et en les ajustant à une grille. JavaScript est ajouté pour faciliter les interactions avec les index temporaires en aidant les utilisateurs de claviers à ignorer plus de 100 éléments. De plus, une requête multimédia expérimentale, prefers-reduced-data, est utilisée pour transformer le défileur multimédia en une expérience de défilement de titres légère.

Commencer par le balisage accessible

Un défilement multimédia ne comporte que quelques composants de base, une liste d'éléments. Dans sa forme la plus simple, une liste peut voyager dans le monde entier et être consommée par tous. Un utilisateur qui accède à cette page peut parcourir une liste et cliquer sur un lien pour afficher un élément. Il s'agit de notre base accessible.

Diffusez une liste avec un élément <ul>:

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

Rendre les éléments de liste interactifs avec un élément <a>:

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

Utilisez un élément <figure> pour représenter sémantiquement une image et sa légende:

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

Notez les attributs alt et loading sur <img>. Le texte alternatif d'un défileur multimédia est une opportunité d'expérience utilisateur pour fournir un contexte supplémentaire à la vignette, ou comme texte de remplacement si l'image ne s'est pas chargée, ou encore pour fournir une UI vocale aux utilisateurs qui s'appuient sur des technologies d'assistance telles qu'un lecteur d'écran. Pour en savoir plus, consultez les cinq règles d'or pour un texte alternatif conforme.

L'attribut loading accepte le mot clé lazy pour indiquer que cette source d'image ne doit être extraite que lorsque l'image se trouve dans le viewport. Cela peut être très utile pour les longues listes, car les utilisateurs ne téléchargent que les images des éléments qu'ils ont vus.

Respecter la préférence de jeu de couleurs de l'utilisateur

Utilisez color-scheme comme balise <meta> pour signaler au navigateur que votre page souhaite utiliser les styles de user-agent clair et sombre. Il s'agit d'un mode sombre sans frais ou d'un mode clair, selon votre apparence:

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

La balise méta fournit le signal le plus précoce possible afin que le navigateur puisse sélectionner une couleur de canevas sombre par défaut si l'utilisateur préfère un thème sombre. Cela signifie que les navigations entre les pages du site ne clignoteront pas d'arrière-plan de canevas blanc entre les chargements. Thème sombre fluide entre les chargements, beaucoup plus agréable pour les yeux.

Pour en savoir plus, consultez Thomas Steiner sur https://web.dev/color-scheme/.

Ajouter du contenu

Compte tenu de la structure de contenu ci-dessus de ul > li > a > figure > picture > img, la tâche suivante consiste à ajouter des images et des titres à faire défiler. J'ai rempli la démonstration d'images et de texte d'espace réservé statiques, mais n'hésitez pas à l'alimenter à partir de votre source de données préférée.

Ajouter un style avec CSS

Il est maintenant temps que le CSS prenne cette liste générique de contenus et la transforme en expérience. Netflix, les plates-formes de téléchargement d'applications et de nombreux autres sites et applications utilisent des zones de défilement horizontal pour remplir le viewport de catégories et d'options.

Créer la mise en page du défilement

Il est important d'éviter de couper le contenu dans les mises en page ou de recourir à la troncation du texte avec des points de suspension. De nombreux téléviseurs sont équipés de défileurs multimédias comme celui-ci, mais ils ont trop souvent recours à des ellipses pour le contenu. Cette mise en page ne le fait pas. Il permet également au contenu multimédia de remplacer la taille de la colonne, ce qui rend une mise en page suffisamment flexible pour gérer de nombreuses combinaisons intéressantes.

Affichage de deux lignes de défilement. L&#39;un n&#39;a pas de points de suspension, ce qui signifie qu&#39;il est plus grand et que chaque titre est entièrement lisible. L&#39;autre est plus court, et de nombreux titres sont tronqués par des points de suspension.

Le conteneur permet de remplacer la taille de la colonne en fournissant la taille par défaut en tant que propriété personnalisée. Cette mise en page de grille est orientée sur la taille des colonnes. Elle ne gère que l'espacement et la direction :

.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;
}

La propriété personnalisée est ensuite utilisée par l'élément <picture> pour créer notre format de base: une zone:

.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);
  }
}

Avec seulement quelques styles mineurs, complétez les bases du conteneur de défilement multimédia:

.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);
  }
}

Le paramètre overflow configure <ul> pour permettre le défilement et la navigation au clavier dans sa liste. Ensuite, le ::marker de chaque élément <li> enfant direct est supprimé en obtenant un nouveau type d'affichage de inline-block.

Les images ne sont pas encore responsives et sortent directement des boîtes dans lesquelles elles se trouvent. Apprivoisez-les avec des tailles, des styles d'ajustement et de bordure, ainsi qu'un dégradé d'arrière-plan pour les chargements différés:

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%)
    );
}

Marge intérieure de défilement

L'alignement sur le contenu de la page, ainsi qu'une surface de défilement de bord à bord, sont essentiels pour obtenir un composant harmonieux et minimaliste.

Pour obtenir la mise en page de défilement bord à bord qui s'aligne sur notre typographie et nos lignes de mise en page, utilisez padding qui correspond à 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 */
}

Correction d'un bug de marge intérieure de défilement horizontal La section ci-dessus montre à quel point il devrait être facile de mettre en place une marge intérieure pour un conteneur de défilement, mais il existe des problèmes de compatibilité non résolus (mais résolus dans Chromium 91 et versions ultérieures). Consultez cette page pour en savoir plus sur l'historique, mais en résumé, la marge intérieure n'était pas toujours prise en compte dans une vue de défilement.

Une zone est mise en surbrillance du côté de la fin en ligne du dernier élément de liste, ce qui montre que la marge intérieure et l&#39;élément ont la même largeur pour créer l&#39;alignement souhaité.

Pour inciter les navigateurs à placer la marge intérieure à la fin du conteneur de défilement, je vais cibler la dernière figure de chaque liste et ajouter un pseudo-élément correspondant à la quantité de marge intérieure souhaitée.

.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);
  }
}

L'utilisation de propriétés logiques permet au conteneur de défilement multimédia de fonctionner dans n'importe quel mode d'écriture et dans n'importe quelle direction de document.

Défilement scroll-snap

Un conteneur à défilement avec dépassement peut devenir une fenêtre d'affichage ancrée avec une ligne de code CSS. Les enfants doivent ensuite spécifier la manière dont ils souhaitent s'aligner sur cette fenêtre d'affichage.

.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;
  }
}

Mise au point

Ce composant s'inspire de sa popularité grandissante sur les téléviseurs, les plates-formes de téléchargement d'applications et plus encore. De nombreuses plates-formes de jeux vidéo utilisent un conteneur de défilement multimédia très semblable à celui-ci, comme disposition principale de l'écran d'accueil. La concentration est un grand moment UX ici, pas seulement un petit ajout. Imaginez que vous utilisiez ce conteneur de défilement multimédia sur votre canapé avec une télécommande, et apportez quelques améliorations mineures à cette interaction:

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

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

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

Cela définit le style de contour de mise au point 7px à l'écart de la zone, ce qui lui laisse un espace agréable. Si l'utilisateur n'a pas de préférences de mouvement concernant la réduction du mouvement, le décalage est mis en transition, ce qui donne un mouvement subtil à l'événement de mise au point.

Indice itinérant

Les utilisateurs de manettes de jeu et de clavier ont besoin d'une attention particulière dans ces longues listes de contenus et d'options à faire défiler. Le modèle courant pour résoudre ce problème est appelé indice itinérant. Il se produit lorsqu'un conteneur d'éléments est sélectionné au clavier, mais qu'un seul enfant est autorisé à le maintenir à la fois. Cette expérience d'un seul élément à la fois est conçue pour permettre de contourner la liste potentiellement longue d'éléments, au lieu d'appuyer sur la touche Tabulation plus de 50 fois pour atteindre la fin.

Ce premier scroller de la démonstration contient 300 éléments. Nous pouvons faire mieux que de les faire traverser toutes pour atteindre la section suivante.

Pour créer cette expérience, JavaScript doit observer les événements de clavier et les événements de focus. J'ai créé une petite bibliothèque Open Source sur npm pour faciliter la réalisation de cette expérience utilisateur. Voici comment l'utiliser pour les trois défileurs :

import {rovingIndex} from 'roving-ux';

rovingIndex({
  element: someElement
});

Cette démonstration interroge le document pour les barres de défilement et appelle la fonction rovingIndex() pour chacune d'elles. Transmettez à rovingIndex() l'élément pour obtenir l'expérience de balayage, comme un conteneur de liste, et un sélecteur de requête cible, au cas où les cibles de focus ne seraient pas des descendants directs.

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

Pour en savoir plus sur cet effet, consultez la bibliothèque Open Source roving-ux.

Aspect-ratio

Au moment de la rédaction de cet article, la compatibilité avec aspect-ratio est derrière un indicateur dans Firefox, mais disponible dans les navigateurs Chromium ou les box. Étant donné que la mise en page de la grille du défileur multimédia ne spécifie que la direction et l'espacement, la taille peut changer dans une requête multimédia qui vérifie la prise en charge du format. Amélioration progressive de certains défileurs multimédias plus dynamiques.

Une boîte au format 4:4 est affichée à côté des autres formats de conception utilisés, à savoir 16:9 et 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 le navigateur est compatible avec la syntaxe aspect-ratio, les images du défileur multimédia sont mises à niveau vers la taille aspect-ratio. Avec la syntaxe d'imbrication de la version préliminaire, le format de chaque image change selon qu'elle se trouve sur la première, la deuxième ou la troisième ligne. La syntaxe imbriquée permet également de définir de petits ajustements de la vue, avec l'autre logique de dimensionnement.

Avec ce CSS, comme la fonctionnalité est disponible dans davantage de moteurs de navigateur, une mise en page facile à gérer, mais plus attrayante visuellement, s'affiche.

Préférence pour les données réduites

Bien que cette technique ne soit disponible que derrière un indicateur dans Canary, je voulais vous expliquer comment j'ai pu réduire considérablement le temps de chargement des pages et l'utilisation des données avec quelques lignes de CSS. La requête multimédia prefers-reduced-data du niveau 5 permet de demander si l'appareil se trouve dans un état de données réduites, comme un mode d'économie de données. Si c\'est le cas, je peux modifier le document et, dans ce cas, masquer les images.

ALT_TEXT_HERE

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

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

Le contenu reste accessible, mais sans le coût du téléchargement des images lourdes. Voici le site avant d'ajouter le CSS prefers-reduced-data:

(7 requêtes, 100 Ko de ressources en 131 ms)

ALT_TEXT_HERE

Voici les performances du site après l'ajout du CSS prefers-reduced-data:

ALT_TEXT_HERE

(71 requêtes, 1,2 Mo de ressources en 1,07 s)

64 requêtes en moins, soit environ 60 images dans le viewport (tests effectués sur un écran grand écran) de cet onglet de navigateur, une augmentation du chargement de la page d'environ 80 % et 10 % des données transmises par ligne. Le CSS est plutôt efficace.

Conclusion

Maintenant que vous savez comment j'ai fait, comment pourriez-vous faire ? 🙂

Diversifions nos approches et découvrons toutes les façons de créer sur le Web. Créez un Codepen ou hébergez votre propre démo, puis envoyez-moi un tweet. Je l'ajouterai à la section "Remix de la communauté" ci-dessous.

Source

Remix de la communauté

Aucun élément à afficher pour l'instant.