Pop-ups: ils font leur grand retour !

L'objectif de l'initiative Open UI est de permettre aux développeurs de proposer des expériences utilisateur de qualité. Pour ce faire, nous essayons de nous attaquer aux modèles les plus problématiques rencontrés par les développeurs. Pour ce faire, nous fournissons de meilleures API et composants intégrés à la plate-forme.

Les pop-ups, décrits dans l'UI Open sous le nom de "Popovers", en font partie.

La réputation des pop-overs est depuis longtemps polarisée. Cela s'explique en partie par la façon dont ils sont créés et déployés. Bien qu'il ne soit pas facile de bien les créer, ils peuvent générer une grande valeur ajoutée en dirigeant les utilisateurs vers certaines choses ou en leur faisant connaître le contenu de votre site, surtout s'ils sont utilisés avec bon goût.

Il existe souvent deux problèmes majeurs lors de la création de popovers:

  • Assurez-vous qu'il est placé au-dessus du reste de votre contenu, à un endroit approprié.
  • Comment la rendre accessible (adaptée au clavier, sélectionnable, etc.)

L'API Popover intégrée offre de différents objectifs, tous ayant le même objectif global : permettre aux développeurs de créer facilement ce modèle. Voici quelques-uns de ces objectifs:

  • Facilitez l'affichage d'un élément et de ses descendants au-dessus du reste du document.
  • Rendez-le accessible.
  • Ne nécessite pas de code JavaScript pour la plupart des comportements courants (fermeture légère, singleton, empilement, etc.).

Vous pouvez consulter la spécification complète des pop-ups sur le site OpenUI.

Compatibilité du navigateur

Où pouvez-vous utiliser l'API Popover intégrée ? Elle est compatible avec Chrome Canary, derrière l'indicateur "Experimental web platform features" au moment de la rédaction de ce document.

Pour activer cet indicateur, ouvrez Chrome Canary et accédez à chrome://flags. Activez ensuite l'option "Experimental web platform features".

Une phase d'évaluation est également disponible pour les développeurs qui souhaitent tester cette fonctionnalité dans un environnement de production.

Enfin, un polyfill est en cours de développement pour l'API. N'oubliez pas de consulter le dépôt à l'adresse github.com/oddbird/popup-polyfill.

Vous pouvez vérifier si des pop-up sont pris en charge à l'aide de l'une des commandes suivantes:

const supported = HTMLElement.prototype.hasOwnProperty("popover");

Solutions actuelles

Que pouvez-vous faire actuellement pour promouvoir vos contenus avant tout le reste ? Si votre navigateur le permet, vous pouvez utiliser l'élément HTML Dialog. Vous devez l'utiliser dans un formulaire "Modal". JavaScript doit être utilisé.

Dialog.showModal();

Il y a quelques points à prendre en compte en matière d'accessibilité. Nous vous recommandons d'utiliser a11y-dialog, par exemple si vous prenez en charge les utilisateurs de Safari version 15.4 ou antérieure.

Vous pouvez également utiliser l'une des nombreuses bibliothèques basées sur des pop-ups, des alertes ou des info-bulles. Beaucoup d'entre eux fonctionnent de la même manière.

  • Ajoutez un conteneur au corps pour afficher les popovers.
  • Modifiez son style pour qu'il se trouve au-dessus de tout le reste.
  • Créez un élément et ajoutez-le au conteneur pour afficher une fenêtre pop-up.
  • Pour le masquer, supprimez l'élément popover du DOM.

Cela nécessite une dépendance supplémentaire et plus de décisions de la part des développeurs. Vous devez également effectuer des recherches pour trouver une offre qui répond à tous vos besoins. L'API Popover vise à répondre à de nombreux scénarios, y compris les info-bulles. L'objectif est de couvrir tous ces scénarios courants, ce qui évite aux développeurs de devoir prendre une autre décision et leur permet de se concentrer sur la création de leurs expériences.

Votre premier pop-up

C'est tout ce dont vous avez besoin.

<div id="my-first-popover" popover>Popover Content!</div>
<button popovertoggletarget="my-first-popover">Toggle Popover</button>

Mais que se passe-t-il ici ?

  • Vous n'avez pas besoin de placer l'élément popover dans un conteneur ni quoi que ce soit : il est masqué par défaut.
  • Vous n'avez pas besoin d'écrire de code JavaScript pour l'afficher. C'est l'attribut popovertoggletarget qui s'en charge.
  • Lorsqu'il apparaît, il est promu à la couche supérieure. Cela signifie qu'il est placé au-dessus de document dans la fenêtre d'affichage. Vous n'avez pas besoin de gérer z-index ni de vous soucier de l'emplacement de votre popover dans le DOM. Il peut être profondément imbriqué dans le DOM, avec des ancêtres de découpage. Vous pouvez également voir les éléments actuellement situés dans la couche supérieure via les outils de développement. Pour en savoir plus sur la couche supérieure, consultez cet article.

GIF montrant la prise en charge de la couche supérieure dans DevTools

  • La fonctionnalité "Light Dismiss" est disponible par défaut. Cela signifie que vous pouvez fermer le pop-up avec un signal de fermeture, par exemple en cliquant en dehors du pop-up, en accédant à un autre élément à l'aide du clavier ou en appuyant sur la touche Échap. Ouvrez-la à nouveau et testez-la.

Quels sont les autres avantages des popovers ? Poussons l'exemple plus loin. Prenons l'exemple de cette démonstration avec du contenu sur la page.

Ce bouton d'action flottant a un positionnement fixe avec un z-index élevé.

.fab {
  position: fixed;
  z-index: 99999;
}

Le contenu du popover est imbriqué dans le DOM, mais lorsque vous l'ouvrez, il est promu au-dessus de cet élément de position fixe. Vous n'avez pas besoin de définir de styles.

Vous remarquerez également que le pop-up comporte désormais un pseudo-élément ::backdrop. Tous les éléments de la couche supérieure se voient attribuer un pseudo-élément ::backdrop stylisé. Cet exemple stylise ::backdrop avec une couleur d'arrière-plan alpha réduite et un filtre de fond, qui floute le contenu sous-jacent.

Mettre en forme un popover

Concentrons-nous à présent sur le style de la fenêtre pop-up. Par défaut, un popover a une position fixe et une marge intérieure appliquée. Il comporte également display: none. Vous pouvez remplacer ce comportement pour afficher un popover. Toutefois, cela ne le placerait pas dans la couche supérieure.

[popover] { display: block; }

Quelle que soit la méthode utilisée pour promouvoir votre popover, vous devrez peut-être le mettre en page ou le positionner une fois qu'il aura été promu à la couche supérieure. Vous ne pouvez pas cibler la couche supérieure et effectuer une action du type

:open {
  display: grid;
  place-items: center;
}

Par défaut, un popover s'affiche au centre du viewport à l'aide de margin: auto. Toutefois, dans certains cas, vous souhaiterez peut-être être explicite concernant le positionnement. Exemple :

[popover] {
  top: 50%;
  left: 50%;
  translate: -50%;
}

Si vous souhaitez mettre en page le contenu de votre popover à l'aide d'une grille CSS ou d'une grille flexbox, il peut être judicieux de l'encapsuler dans un élément. Sinon, vous devrez déclarer une règle distincte qui modifie display une fois que le pop-up se trouve dans la couche supérieure. Si vous le définissez par défaut, il s'affichera par défaut en remplaçant display: none.

[popover]:open {
 display: flex;
}

Si vous avez essayé cette démonstration, vous remarquerez que le popover s'affiche et se ferme désormais. Vous pouvez faire apparaître et disparaître des popovers à l'aide du pseudo-sélecteur :open. Le pseudo-sélecteur :open correspond aux popovers affichés (et donc dans la couche supérieure).

Cet exemple utilise une propriété personnalisée pour piloter la transition. Vous pouvez également appliquer une transition à l'::backdrop du popover.

[popover] {
  --hide: 1;
  transition: transform 0.2s;
  transform: translateY(calc(var(--hide) * -100vh))
            scale(calc(1 - var(--hide)));
}

[popover]::backdrop {
  transition: opacity 0.2s;
  opacity: calc(1 - var(--hide, 1));
}


[popover]:open::backdrop  {
  --hide: 0;
}

Nous vous conseillons de regrouper les transitions et les animations dans une requête multimédia pour le mouvement. Cela peut également aider à maintenir votre timing. En effet, vous ne pouvez pas partager de valeurs entre popover et ::backdrop via une propriété personnalisée.

@media(prefers-reduced-motion: no-preference) {
  [popover] { transition: transform 0.2s; }
  [popover]::backdrop { transition: opacity 0.2s; }
}

Jusqu'à présent, vous avez vu l'utilisation de popovertoggletarget pour afficher un pop-up. Pour l'ignorer, nous utilisons "Ignorer". Vous pouvez également utiliser les attributs popovershowtarget et popoverhidetarget. Ajoutons un bouton à un pop-up qui le masque et remplaçons le bouton d'activation par popovershowtarget.

<div id="code-popover" popover>
  <button popoverhidetarget="code-popover">Hide Code</button>
</div>
<button popovershowtarget="code-popover">Reveal Code</button>

Comme indiqué précédemment, l'API Popover ne se limite pas à notre notion historique de pop-up. Vous pouvez créer des composants pour tous les types de scénarios, tels que les notifications, les menus, les info-bulles, etc.

Certains de ces scénarios nécessitent des modèles d'interaction différents. Interactions telles que le pointage. L'utilisation d'un attribut popoverhovertarget a fait l'objet de tests, mais n'est pas implémentée actuellement.

<div popoverhovertarget="hover-popover">Hover for Code</div>

L'idée est de passer la souris sur un élément pour afficher la cible. Ce comportement peut être configuré via des propriétés CSS. Ces propriétés CSS définissent la période pendant laquelle l'utilisateur peut pointer sur un élément auquel un popover réagit. Le comportement par défaut testé consistait à afficher un popover après un 0.5s explicite de :hover. Ensuite, il faudrait une légère fermeture ou l'ouverture d'un autre pop-up pour fermer (plus d'informations à venir sur ce sujet). Cela était dû au fait que la durée de masquage du pop-up était définie sur Infinity.

En attendant, vous pouvez utiliser JavaScript pour polyfiller cette fonctionnalité.

let hoverTimer;
const HOVER_TRIGGERS = document.querySelectorAll("[popoverhovertarget]");
const tearDown = () => {
  if (hoverTimer) clearTimeout(hoverTimer);
};
HOVER_TRIGGERS.forEach((trigger) => {
  const popover = document.querySelector(
    `#${trigger.getAttribute("popoverhovertarget")}`
  );
  trigger.addEventListener("pointerenter", () => {
    hoverTimer = setTimeout(() => {
      if (!popover.matches(":open")) popover.showPopOver();
    }, 500);
    trigger.addEventListener("pointerleave", tearDown);
  });
});

L'avantage de définir un élément comme une fenêtre de survol explicite est qu'il garantit que l'action de l'utilisateur est intentionnelle (par exemple, un utilisateur passe son pointeur sur une cible). Nous ne voulons pas afficher le pop-up, sauf si c'est son intention.

Essayez cette démonstration, dans laquelle vous pouvez pointer sur la cible lorsque la fenêtre est définie sur 0.5s.

.

Avant d'examiner quelques cas d'utilisation courants et des exemples, examinons quelques points.


Types de popover

Nous avons vu le comportement des interactions non JavaScript. Mais qu'en est-il du comportement des popovers dans leur ensemble ? Que faire si vous ne souhaitez pas utiliser la fonctionnalité "Ignorer avec un appui léger" ? Vous souhaitez appliquer un modèle Singleton à vos popovers ?

L'API Popover vous permet de spécifier trois types de popovers dont le comportement diffère.

[popover=auto]/[popover] :

  • Prise en charge de l'imbrication. Cela ne signifie pas non plus qu'ils sont uniquement imbriqués dans le DOM. Voici la définition d'un pop-over ancestral :
    • en fonction de la position DOM (enfant).
    • liés par des attributs de déclenchement sur des éléments enfants tels que popovertoggletarget, popovershowtarget, etc.
    • liés par l'attribut anchor (API d'ancrage CSS en cours de développement).
  • Fermeture légère.
  • L'ouverture ferme les autres popovers qui ne sont pas des popovers ancestraux. Essayez la démo ci-dessous qui montre comment fonctionne l'imbrication avec des popovers ancestraux. Découvrez comment la modification de certaines instances popoverhidetarget/popovershowtarget en popovertoggletarget modifie les choses.
  • Si vous appuyez sur la touche Éclair pour fermer une notification, toutes les notifications sont supprimées. Si vous appuyez sur la touche Éclair pour fermer une notification de la pile, seules les notifications situées au-dessus de celle-ci sont supprimées.

[popover=manual] :

  • Les autres fenêtres pop-up ne sont pas fermées.
  • Aucune lumière ne s'éteint.
  • Nécessite une fermeture explicite via un élément de déclencheur ou JavaScript.

API JavaScript

Lorsque vous avez besoin de mieux contrôler vos popovers, vous pouvez utiliser JavaScript. Vous obtenez à la fois une méthode showPopover et une méthode hidePopover. Vous devez également écouter les événements popovershow et popoverhide:

Afficher un pop-up js popoverElement.showPopover() Masquer un pop-up:

popoverElement.hidePopover()

Écouter si un pop-up s'affiche:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Écoutez l'affichage d'un popover et annulez son affichage:

popoverElement.addEventListener('popovershow',event => {
  event.preventDefault();
  console.warn(We blocked a popover from being shown);
})

Écoutez si un pop-over est masqué:

popoverElement.addEventListener('popoverhide', doSomethingWhenPopoverHides)

Vous ne pouvez pas annuler le masquage d'un pop-up:

popoverElement.addEventListener('popoverhide',event => {
  event.preventDefault();
  console.warn("You aren't allowed to cancel the hiding of a popover");
})

Vérifiez si un popover se trouve dans la couche supérieure:

popoverElement.matches(':open')

Cela fournit une puissance supplémentaire pour certains scénarios moins courants. Par exemple, affichez un pop-up après une période d'inactivité.

Cette démonstration comporte des pop-ups avec des sons audibles. Nous aurons donc besoin de JavaScript pour lire l'audio. Lorsque l'utilisateur clique, le pop-up est masqué, l'audio est lu, puis le pop-up s'affiche à nouveau.

Accessibilité

L'accessibilité est au cœur de la conception de l'API Popover. Les mappages d'accessibilité associent le popover à son élément déclencheur, si nécessaire. Cela signifie que vous n'avez pas besoin de déclarer des attributs aria-* tels que aria-haspopup, en supposant que vous utilisez l'un des attributs de déclenchement comme popovertoggletarget.

Pour gérer la sélection, vous pouvez utiliser l'attribut "autofocus" afin de déplacer la sélection vers un élément dans un popover. Il s'agit de la même chose qu'avec une boîte de dialogue, mais la différence apparaît lorsque vous rétablissez la sélection, en raison de la fermeture légère. Dans la plupart des cas, la fermeture d'un popover rétablit la sélection sur l'élément précédemment sélectionné. Toutefois, le focus est déplacé vers un élément cliqué lors de la fermeture légère, s'il peut être sélectionné. Consultez la section sur la gestion de la concentration dans la vidéo explicative.

Vous devez ouvrir la version plein écran de cette démonstration pour qu'elle fonctionne.

Dans cette démonstration, l'élément sélectionné est entouré d'un contour vert. Essayez de faire défiler l'interface avec votre clavier. Notez où la mise au point est renvoyée lorsqu'un popover est fermé. Vous remarquerez peut-être également que si vous avez appuyé sur l'onglet "À propos", le pop-up s'est fermé. C'est volontaire. Bien que les pop-ups permettent de gérer le focus, ils ne piégeent pas le focus. La navigation au clavier identifie un signal de fermeture lorsque le curseur sort du pop-up.

Ancrage (en cours de développement)

En ce qui concerne les popovers, l'ancrage de l'élément à son déclencheur est un modèle difficile à gérer. Par exemple, si une info-bulle est définie pour s'afficher au-dessus de son déclencheur, mais que le document est fait défiler. Cette info-bulle risque d'être tronquée par la fenêtre d'affichage. Il existe actuellement des offres JavaScript pour y remédier, comme l'interface utilisateur flottante. Ils repositionneront l'info-bulle pour que cela ne se reproduise plus et s'appuieront sur l'ordre de position souhaité.

Toutefois, nous souhaitons que vous puissiez définir cela avec vos styles. Une API complémentaire est en cours de développement avec l'API Popover pour résoudre ce problème. L'API CSS Anchor Positioning vous permet d'attacher des éléments à d'autres éléments. Elle le fait de manière à repositionner les éléments afin qu'ils ne soient pas coupés par le viewport.

Cette démonstration utilise l'API Anchoring dans son état actuel. La position du bateau répond à la position de l'ancre dans la fenêtre d'affichage.

Voici un extrait du code CSS permettant de faire fonctionner cette démonstration. Aucun JavaScript requis.

.anchor {
  --anchor-name: --anchor;
}
.anchored {
  position: absolute;
  position-fallback: --compass;
}
@position-fallback --compass {
  @try {
    bottom: anchor(--anchor top);
    left: anchor(--anchor right);
  }
  @try {
    top: anchor(--anchor bottom);
    left: anchor(--anchor right);
  }
}

Vous pouvez consulter les spécifications ici. Un polyfill est également disponible pour cette API.

Exemples

Maintenant que vous savez ce qu'est un popover et comment l'utiliser, examinons quelques exemples.

Notifications

Cette démonstration montre la notification "Copier dans le presse-papiers".

  • Fuseau horaire : [popover=manual].
  • Sur l'action, affichez le pop-up avec showPopover.
  • Après un délai avant expiration de 2000ms, masquez-le avec hidePopover.

Notifications toast

Cette démonstration utilise la couche supérieure pour afficher des notifications de type toast.

  • Un popover de type manual sert de conteneur.
  • Les nouvelles notifications sont ajoutées au pop-up et celui-ci s'affiche.
  • Ils sont supprimés avec l'API Web Animations au clic et supprimés du DOM.
  • Si aucun message ne s'affiche, le pop-up est masqué.

Menu imbriqué

Cette démonstration montre comment un menu de navigation imbriqué peut fonctionner.

  • Utilisez [popover=auto], car il autorise les pop-ups imbriqués.
  • Utilisez autofocus sur le premier lien de chaque liste déroulante pour naviguer avec le clavier.
  • Il s'agit d'un candidat idéal pour l'API d'ancrage CSS. Toutefois, pour cette démonstration, vous pouvez utiliser une petite quantité de code JavaScript pour mettre à jour les positions à l'aide de propriétés personnalisées.
const ANCHOR = (anchor, anchored) => () => {
  const { top, bottom, left, right } = anchor.getBoundingClientRect();
  anchored.style.setProperty("--top", top);
  anchored.style.setProperty("--right", right);
  anchored.style.setProperty("--bottom", bottom);
  anchored.style.setProperty("--left", left);
};

PRODUCTS_MENU.addEventListener("popovershow", ANCHOR(PRODUCT_TARGET, PRODUCTS_MENU));

N'oubliez pas que, comme cette démonstration utilise autofocus, vous devez l'ouvrir en plein écran pour pouvoir naviguer à l'aide du clavier.

Pop-over multimédia

Cette démonstration montre comment afficher des contenus multimédias.

  • Utilise [popover=auto] pour la fermeture légère.
  • JavaScript écoute l'événement play de la vidéo et l'affiche.
  • L'événement popoverhide des popovers met la vidéo en pause.

Pop-ups de style Wiki

Cette démonstration montre comment créer des info-bulles de contenu intégrées contenant des contenus multimédias.

  • Fuseau horaire : [popover=auto]. En montrant l'un, les autres sont masqués, car ils ne sont pas ancêtres.
  • Affichée sur pointerenter avec JavaScript.
  • Un autre candidat idéal pour l'API d'ancrage CSS.

Cette démonstration crée un panneau de navigation à l'aide d'un pop-up.

  • Utilise [popover=auto] pour la fermeture légère.
  • Utilise autofocus pour sélectionner le premier élément de navigation.

Gérer les arrière-plans

Cette démonstration montre comment gérer les arrière-plans de plusieurs popovers pour lesquels vous ne souhaitez qu'un seul ::backdrop soit visible.

  • Utilisez JavaScript pour gérer une liste des fenêtres pop-up visibles.
  • Appliquez un nom de classe au pop-up le plus bas de la couche supérieure.
.

Pop-up de curseur personnalisé

Cette démonstration montre comment utiliser popover pour promouvoir un canvas sur la couche supérieure et l'utiliser pour afficher un curseur personnalisé.

  • Mettez en avant canvas en tant que couche supérieure avec showPopover et [popover=manual].
  • Lorsque d'autres pop-ups sont ouverts, masquez et affichez le pop-up canvas pour vous assurer qu'il se trouve au-dessus.

Pop-over de la feuille d'action

Cette démonstration montre comment utiliser un popover comme une actionsheet.

  • Affichez le pop-up par défaut en ignorant display.
  • La feuille d'action s'ouvre avec le déclencheur du pop-up.
  • Lorsque la fenêtre contextuelle s'affiche, elle est placée dans la couche supérieure et convertie en vue.
  • Vous pouvez utiliser la fermeture légère pour le renvoyer.

Popover activé par le clavier

Cette démonstration montre comment utiliser le pop-up pour l'interface utilisateur de style palette de commandes.

  • Appuyez sur cmd+j pour afficher le pop-up.
  • Le input est mis au point avec autofocus.
  • La zone de liste déroulante est un deuxième popover placé sous l'entrée principale.
  • La fermeture légère ferme la palette si le menu déroulant n'est pas présent.
  • Autre candidat pour l'API d'ancrage

Pop-over temporisé

Cette démonstration affiche un pop-up d'inactivité au bout de quatre secondes. Modèle d'interface utilisateur souvent utilisé dans les applications contenant des informations sécurisées sur un utilisateur pour afficher une fenêtre modale de déconnexion.

  • Utilisez JavaScript pour afficher le pop-up après une période d'inactivité.
  • Lors de l'affichage du pop-up, réinitialisez le minuteur.

Éco. d'écran

Comme dans la démonstration précédente, vous pouvez ajouter un peu de fantaisie à votre site et ajouter un écran de veille.

  • Utilisez JavaScript pour afficher le pop-up après une période d'inactivité.
  • Balayez l'écran pour masquer et réinitialiser le minuteur.
.

Suivi du point d'insertion

Cette démonstration montre comment faire en sorte qu'un pop-over suive un curseur de saisie.

  • Afficher le pop-up en fonction de la sélection, d'un événement clé ou de la saisie de caractères spéciaux.
  • Utilisez JavaScript pour mettre à jour la position de la fenêtre pop-up avec des propriétés personnalisées limitées.
  • Ce modèle nécessite une réflexion approfondie sur le contenu affiché et l'accessibilité.
  • Elle est souvent présente dans l'UI d'édition de texte et dans les applications dans lesquelles vous pouvez ajouter des tags.

Menu du bouton d'action flottant

Cette démonstration montre comment utiliser un popover pour implémenter un menu de bouton d'action flottant sans JavaScript.

  • Promouvoir un popover de type manual avec la méthode showPopover Il s'agit du bouton principal.
  • Le menu est un autre pop-up qui est la cible du bouton principal.
  • Le menu s'ouvre avec popovertoggletarget.
  • Utilisez autofocus pour mettre en surbrillance le premier élément de menu à afficher.
  • L'option "Fermer" permet de fermer le menu.
  • La torsion de l'icône utilise :has(). Pour en savoir plus sur :has(), consultez cet article.

Et voilà !

Voici une présentation des pop-ups, qui seront disponibles dans le cadre de l'initiative Open UI. Utilisé de manière judicieuse, il sera un ajout fantastique à la plate-forme Web.

N'oubliez pas de consulter Ouvrir l'UI. La présentation du pop-up est mise à jour au fur et à mesure de l'évolution de l'API. Et voici la collection de toutes les démonstrations.

Merci de nous avoir fait un "saut" !


Photo par Madison Oren, publiée sur Unsplash