Créer un composant "Stories"

Présentation de base sur la création d'une expérience semblable à celle des stories Instagram sur le Web.

Dans cet article, je souhaite partager mes réflexions sur la création d'un composant Stories pour le Web qui est responsif, compatible avec la navigation au clavier et compatible avec tous les navigateurs.

Démo

Si vous préférez une démonstration pratique de la création de ce composant Stories, consultez l'atelier de programmation sur le composant Stories.

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

Présentation

Les stories Snapchat et Instagram (sans oublier les fleets) sont deux exemples populaires de l'expérience utilisateur des stories. En termes d'expérience utilisateur, les stories sont généralement un modèle axé sur les pressions, réservé aux mobiles et permettant de naviguer dans plusieurs abonnements. Par exemple, sur Instagram, les utilisateurs ouvrent l'histoire d'un ami et parcourent les images qu'elle contient. Ils le font généralement avec plusieurs amis à la fois. En appuyant sur le côté droit de l'appareil, l'utilisateur passe à la prochaine story de cet ami. En balayant l'écran vers la droite, l'utilisateur passe à un autre ami. Un composant Story est assez semblable à un carrousel, mais il permet de naviguer dans un tableau multidimensionnel plutôt que dans un tableau unidimensionnel. C'est comme s'il y avait un carrousel dans chaque carrousel. 🤯

Tableau multidimensionnel visualisé à l'aide de fiches. De gauche à droite, une pile de cartes à bordure violette, et à l'intérieur de chaque carte, une ou plusieurs cartes à bordure cyan. Liste dans une liste.
1er carrousel d'amis
2e carrousel "empilé" d'histoires
👍 Liste dans une liste, également appelée tableau multidimensionnel

Choisir les outils adaptés à la tâche

Dans l'ensemble, j'ai trouvé que ce composant était assez simple à créer, grâce à quelques fonctionnalités essentielles de la plate-forme Web. Voyons-les.

Grille CSS

Notre mise en page ne s'est pas avérée difficile pour CSS Grid, car il est équipé de méthodes efficaces pour gérer le contenu.

Mise en page des amis

Notre wrapper de composant .stories principal est une vue de défilement horizontale mobile first:

.stories {
  inline-size: 100vw;
  block-size: 100vh;

  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;

  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}

/* desktop constraint */
@media (hover: hover) and (min-width: 480px) {
  max-inline-size: 480px;
  max-block-size: 848px;
}
Utilisation du mode Appareil des outils pour les développeurs Chrome pour mettre en surbrillance les colonnes créées par la grille

Décomposons cette mise en page grid:

  • Nous remplissons explicitement la fenêtre d'affichage sur mobile avec 100vh et 100vw, et nous limitons la taille sur ordinateur.
  • / sépare nos modèles de lignes et de colonnes
  • auto-flow est traduit par grid-auto-flow: column
  • Le modèle de flux automatique est 100%, qui correspond dans ce cas à la largeur de la fenêtre de défilement.

Sur un téléphone mobile, considérez la taille de ligne comme la hauteur de la fenêtre d'affichage et chaque colonne comme la largeur de la fenêtre d'affichage. Poursuivant l'exemple des stories Snapchat et Instagram, chaque colonne correspond à la story d'un ami. Nous voulons que les stories de vos amis continuent en dehors de la fenêtre d'affichage afin que vous ayez un endroit où faire défiler l'écran. Grid crée autant de colonnes qu'il en faut pour mettre en page votre code HTML pour chaque histoire d'un ami, créant ainsi un conteneur de défilement dynamique et responsif. La grille nous a permis de centraliser l'ensemble de l'effet.

Empilement

Pour chaque ami, nous avons besoin que ses stories soient prêtes à être paginées. Pour préparer l'animation et d'autres motifs amusants, j'ai choisi une pile. Lorsque je parle de pile, je veux dire que vous regardez un sandwich de haut en bas, et non de côté.

Avec la grille CSS, nous pouvons définir une grille à cellule unique (c'est-à-dire un carré), où les lignes et les colonnes partagent un alias ([story]), puis chaque enfant est attribué à cet espace à cellule unique associé à un alias:

.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
.story {
  grid-area: story;
  background-size: cover;
  
}

Cela permet à notre code HTML de contrôler l'ordre d'empilement et de maintenir tous les éléments dans le flux. Notez que nous n'avons pas eu à faire quoi que ce soit avec le positionnement absolute ou z-index, et que nous n'avons pas eu à corriger la mise en forme avec height: 100% ou width: 100%. La grille parente a déjà défini la taille du viewport de l'image de la story. Aucun de ces composants de la story n'a donc besoin d'être invité à le remplir.

Points d'ancrage de défilement CSS

La spécification des points d'ancrage de défilement CSS permet de verrouiller facilement les éléments dans la fenêtre d'affichage lors du défilement. Avant l'existence de ces propriétés CSS, vous deviez utiliser JavaScript, et c'était… délicat, pour le moins. Consultez Présentation des points d'ancrage de défilement CSS de Sarah Drasner pour découvrir comment les utiliser.

Défilement horizontal avec et sans styles scroll-snap-points. Sans elle, les utilisateurs peuvent faire défiler l'écran librement. Le navigateur repose alors doucement sur chaque élément.
parent
.stories {
  display: grid;
  grid: 1fr / auto-flow 100%;
  gap: 1ch;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  touch-action: pan-x;
}
Le parent avec défilement hors limites définit le comportement d'ancrage.
enfant
.user {
  display: grid;
  grid: [story] 1fr / [story] 1fr;
  scroll-snap-align: start;
  scroll-snap-stop: always;
}
Les enfants peuvent activer la fonctionnalité de cible de capture.

J'ai choisi les points de repère de défilement pour plusieurs raisons:

  • Accès sans frais La spécification des points d'ancrage de défilement indique que l'appui sur les touches Flèche vers la gauche et Flèche vers la droite doit faire défiler les points d'ancrage par défaut.
  • Spécification en constante évolution : la spécification des points de repère de défilement est en constante évolution, ce qui signifie que mon composant Stories ne peut qu'être amélioré à l'avenir.
  • Facilité d'implémentation Les points de repère de défilement sont conçus pour le cas d'utilisation de la pagination horizontale axée sur le toucher.
  • Inertie de type plate-forme libre Chaque plate-forme défilera et se reposera dans son style, contrairement à l'inertie normalisée, qui peut avoir un style de défilement et de repos étrange.

Compatibilité multinavigateur

Nous avons effectué des tests sur Opera, Firefox, Safari et Chrome, ainsi que sur Android et iOS. Voici un bref récapitulatif des fonctionnalités Web pour lesquelles nous avons constaté des différences de fonctionnalités et de prise en charge.

Cependant, certains CSS ne s'appliquent pas, ce qui fait que certaines plates-formes ne bénéficient pas actuellement d'optimisations de l'expérience utilisateur. J'ai apprécié de ne pas avoir à gérer ces fonctionnalités et je suis convaincu qu'elles finiront par arriver sur d'autres navigateurs et plates-formes.

scroll-snap-stop

Les carrousels étaient l'un des principaux cas d'utilisation de l'expérience utilisateur qui ont conduit à la création de la spécification CSS Scroll Snap Points. Contrairement aux stories, un carrousel n'a pas toujours besoin de s'arrêter sur chaque image après qu'un utilisateur a interagi avec elle. Il peut être acceptable ou encouragé de faire défiler rapidement le carrousel. En revanche, il est préférable de naviguer dans les stories une par une, et c'est exactement ce que scroll-snap-stop fournit.

.user {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

Au moment de la rédaction de cet article, scroll-snap-stop n'est compatible qu'avec les navigateurs basés sur Chromium. Consultez la page Compatibilité des navigateurs pour en savoir plus. Ce n'est toutefois pas un problème bloquant. Cela signifie simplement que sur les navigateurs non compatibles, les utilisateurs peuvent ignorer accidentellement un ami. Les utilisateurs devront donc être plus prudents, ou nous devrons écrire du code JavaScript pour nous assurer qu'un ami ignoré n'est pas marqué comme vu.

Pour en savoir plus, consultez la spécification.

overscroll-behavior

Avez-vous déjà fait défiler une fenêtre modale lorsque vous avez soudainement commencé à faire défiler le contenu derrière la fenêtre modale ? overscroll-behavior permet au développeur de piéger le défilement et de ne jamais le laisser partir. Il est adapté à toutes sortes d'occasions. Le composant "Mes stories" l'utilise pour empêcher les balayages et les gestes de défilement supplémentaires de quitter le composant.

.stories {
  overflow-x: auto;
  overscroll-behavior: contain;
}

Safari et Opera étaient les deux navigateurs qui n'étaient pas compatibles avec cette fonctionnalité, et c'est tout à fait acceptable. Ces utilisateurs bénéficieront d'une expérience de défilement comme à leur habitude et ne remarqueront peut-être jamais cette amélioration. Personnellement, je suis un grand fan et j'aime l'inclure dans presque toutes les fonctionnalités de défilement excessif que j'implémente. Il s'agit d'un ajout inoffensif qui ne peut qu'améliorer l'expérience utilisateur.

scrollIntoView({behavior: 'smooth'})

Lorsqu'un utilisateur appuie ou clique et qu'il a atteint la fin de l'ensemble d'histoires d'un ami, il est temps de passer au prochain ami dans l'ensemble de points d'ancrage de défilement. Avec JavaScript, nous avons pu faire référence au prochain ami et demander à ce qu'il soit affiché à l'écran. La compatibilité avec les principes de base est excellente. Chaque navigateur a fait défiler l'élément pour l'afficher. Cependant, tous les navigateurs ne l'ont pas fait 'smooth'. Cela signifie simplement qu'il est affiché à l'écran au lieu d'être épinglé.

element.scrollIntoView({
  behavior: 'smooth'
})

Safari était le seul navigateur à ne pas prendre en charge behavior: 'smooth'. Consultez la page Compatibilité des navigateurs pour en savoir plus.

Pratique

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 Glitch, tweetez-moi votre version, et je l'ajouterai à la section Remix de la communauté ci-dessous.

Remix de la communauté