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 résoudre les problèmes plus complexes auxquels sont confrontés les développeurs. Pour ce faire, nous pouvons fournir de meilleurs composants et API intégrés à la plate-forme.

Les fenêtres pop-up font partie de ces problèmes. Dans l'interface utilisateur ouverte, elles sont désignées par le terme "Fenêtres pop-up".

La réputation des pop-overs est longtemps polarisante. Cela est en partie dû à la façon dont ils sont à la fois compilés et déployés. Bien qu'il ne soit pas facile de créer des modèles faciles à créer, ils peuvent générer beaucoup de valeur 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.

La création de pop-overs pose souvent deux problèmes majeurs:

  • Comment s'assurer qu'elle est placée 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-les accessibles.
  • JavaScript ne nécessite pas l'utilisation de JavaScript pour les comportements les plus courants (Light close, singleton, stacking, etc.).

Vous pouvez consulter les spécifications complètes relatives aux fenêtres pop-up sur le site OpenUI.

Compatibilité du navigateur

Où pouvez-vous utiliser l'API Popover intégrée ? Il est compatible avec Chrome Canary, derrière les fonctionnalités expérimentales de la plate-forme Web lors de la rédaction de cet indicateur.

Pour activer cet indicateur, ouvrez Chrome Canary et accédez à chrome://flags. Activez ensuite "Fonctionnalités expérimentales de la plate-forme Web" .

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 disponibles à l'aide de l'une des commandes suivantes:

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

Solutions actuelles

Que pouvez-vous faire actuellement pour promouvoir votre contenu avant tout le reste ? Si votre navigateur le permet, vous pouvez utiliser l'élément de boîte de dialogue HTML. Il faudrait l'utiliser dans "Modal" . JavaScript doit être utilisé.

Dialog.showModal();

Il faut tenir compte de certains aspects en termes d'accessibilité. Il est recommandé d'utiliser a11y-dialog, par exemple s'il est destiné aux utilisateurs de Safari antérieurs à la version 15.4.

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 ont tendance à fonctionner de la même manière.

  • Ajouter un conteneur au corps pour afficher les pop-ups.
  • Personnalisez-le pour qu'il apparaisse au-dessus des autres.
  • Créez un élément et ajoutez-le au conteneur pour afficher une fenêtre pop-up.
  • Masquez-le en supprimant l'élément contextuel du DOM.

Cela nécessite une dépendance supplémentaire et plus de décisions pour les développeurs. Il faut également faire des recherches pour trouver une offre qui réponde à tout ce dont vous avez besoin. L'API Popover est conçue pour de nombreux scénarios, y compris pour les info-bulles. L'objectif est de couvrir tous ces scénarios courants, afin d'éviter aux développeurs d'avoir à prendre une autre décision pour qu'ils puissent 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 pop-up 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 le faire apparaître. Cette opération est gérée par l'attribut popovertoggletarget.
  • Lorsqu'il apparaît, il est promu dans 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 pop-up dans le DOM. Elle peut être imbriquée dans le DOM en profondeur, avec des ancêtres rognants. Vous pouvez également voir quels éléments se trouvent actuellement 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 compatibilité de la couche supérieure des outils de développement

  • L'option "Light Dismiss" s'affiche. prêt à l'emploi. En d'autres termes, vous pouvez fermer la fenêtre contextuelle à l'aide d'un signal de fermeture, par exemple en cliquant à l'extérieur de la fenêtre pop-up, en accédant à un autre élément avec le clavier ou en appuyant sur la touche Échap. Rouvrez-le et essayez-le !

Qu'obtient-on d'autre avec le pop-over ? Poussons l'exemple plus loin. Considérez cette démonstration avec du contenu sur la page.

Le positionnement du bouton d'action flottant est fixe avec une z-index élevée.

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

Le contenu de la fenêtre contextuelle est imbriqué dans le DOM, mais lorsque vous l'ouvrez, il est placé au-dessus de cet élément de position fixe. Vous n'avez pas besoin de définir de style.

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 applique un style à ::backdrop avec une couleur d'arrière-plan alpha réduite et un filtre d'arrière-plan qui floute le contenu sous-jacent.

Styliser un pop-up

Concentrons-nous à présent sur le style de la fenêtre pop-up. Par défaut, une fenêtre contextuelle a une position fixe et une marge intérieure est appliquée. Il contient également display: none. Vous pouvez ignorer cette option pour afficher un pop-up. Mais cela ne le ferait pas passer à la couche supérieure.

[popover] { display: block; }

Quelle que soit la façon dont vous faites la promotion de votre pop-up, une fois que vous l'avez placée dans la couche supérieure, vous devrez peut-être la mettre en page ou la positionner. 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 pop-up s'affiche au centre de la fenêtre d'affichage à 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 disposer le contenu dans la fenêtre pop-up à l'aide d'une grille CSS ou d'un Flexbox, il peut être judicieux de l'encapsuler dans un élément. Sinon, vous devez déclarer une règle distincte qui modifie display une fois que le pop-up se trouve dans la couche supérieure. Si vous définissez cette option par défaut, elle s'affiche en remplaçant display: none par défaut.

[popover]:open {
 display: flex;
}

Si vous avez essayé cette version de démonstration, vous remarquerez que la fenêtre pop-up est en cours de transition. Vous pouvez ajouter et supprimer des pop-ups à l'aide du pseudo-sélecteur :open. Le pseudo-sélecteur :open correspond aux pop-overs qui s'affichent (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 au ::backdrop du pop-up.

[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 ici de regrouper les transitions et les animations dans une requête média portant sur 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 l'option "Fermer le message". Mais vous disposez également des attributs popovershowtarget et popoverhidetarget que vous pouvez utiliser. 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 conception historique des pop-ups. Vous pouvez créer des applications pour tous les types de scénarios, tels que les notifications, les menus, les info-bulles, etc.

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

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

L'idée est de pointer sur un élément pour afficher la cible. Ce comportement peut être configuré via les propriétés CSS. Ces propriétés CSS définissent la fenêtre de temps pour le survol d'un élément auquel une fenêtre pop-up réagit. Pour le comportement par défaut testé, une émission pop-over s'affiche après une 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 émuler 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 une fenêtre de survol explicite est qu'elle 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 : vous pouvez pointer sur la cible avec la fenêtre définie sur 0.5s.


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


Types de pop-ups

Nous avons abordé les comportements d'interaction non-JavaScript. Mais qu'en est-il du comportement des pop-ups dans son ensemble ? Que faire si vous ne voulez pas de l'option "Désactiver le mode clair" ? Ou vous voulez appliquer un motif singleton à vos pop-overs ?

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

[popover=auto]/[popover] :

  • Prise en charge de l'imbrication. Cela ne signifie pas qu'être imbriqué dans le DOM uniquement. Voici la définition d'un pop-over ancestral: <ph type="x-smartling-placeholder">
      </ph>
    • en fonction de la position DOM (enfant).
    • en déclenchant des attributs sur les éléments enfants tels que popovertoggletarget, popovershowtarget, etc.
    • par l'attribut anchor (API d'ancrage CSS en cours de développement).
  • Fermer la lumière
  • L'ouverture entraîne la fermeture des autres fenêtres pop-up qui ne sont pas des pop-overs ancêtres. Jouez avec la démo ci-dessous qui montre comment fonctionne l'imbrication avec des pop-overs ancêtres. Découvrez en quoi le fait de remplacer certaines des instances popoverhidetarget/popovershowtarget par popovertoggletarget a une incidence sur les changements.
  • Si vous en supprimez un seul, tout le contenu est ignoré. En revanche, si vous en supprimez un, seuls ceux qui se trouvent au-dessus dans la pile sont ignorés.

[popover=manual] :

  • Les autres fenêtres pop-up ne sont pas fermées.
  • Éteindre le voyant
  • Nécessite une fermeture explicite via un élément déclencheur ou JavaScript.

API JavaScript

Lorsque vous avez besoin de mieux contrôler vos fenêtres pop-up, vous pouvez procéder à l'analyse à l'aide de JavaScript. Vous obtenez à la fois une méthode showPopover et une méthode hidePopover. Vous avez également des événements popovershow et popoverhide à écouter:

Afficher un pop-up js popoverElement.showPopover() Pour masquer une fenêtre contextuelle:

popoverElement.hidePopover()

Écouter si un pop-up s'affiche:

popoverElement.addEventListener('popovershow', doSomethingWhenPopoverShows)

Écoutez l'affichage d'un pop-up et annulez l'affichage:

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

Écoutez si un pop-up 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 une fenêtre contextuelle se trouve dans la couche supérieure:

popoverElement.matches(':open')

Vous bénéficiez ainsi d'une puissance supplémentaire pour des scénarios moins courants. Vous pouvez par exemple afficher un pop-up après une période d'inactivité.

Cette démo comporte des pop-ups avec des pop-ups audibles. Nous avons donc besoin de JavaScript pour lire le contenu audio. Lors d'un clic, le pop-up est masqué, la lecture du contenu audio est lue, puis affichée à nouveau.

Accessibilité

Avec l'API Popover, l'accessibilité est au premier plan de la réflexion. Les mappages d'accessibilité associent le pop-up à 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 la gestion du focus, vous pouvez utiliser l'attribut autofocus pour sélectionner un élément dans une fenêtre contextuelle. C'est la même chose que pour une boîte de dialogue, mais la différence vient lorsque l'utilisateur revient au premier plan, en raison de l'arrêt de la lumière. Dans la plupart des cas, la fermeture d'une fenêtre contextuelle remet le focus à l'élément précédemment sélectionné. Toutefois, la mise au point est déplacée vers un élément sur lequel l'utilisateur clique à l'arrêt du flash, s'il peut se concentrer. Consultez la section sur la gestion du ciblage dans la vidéo explicative.

Vous devez ouvrir la version plein écran. de cette démonstration.

Dans cette démonstration, l'élément sélectionné est entouré d'un contour vert. Essayez de naviguer dans l'interface à l'aide de votre clavier. Notez où le curseur est renvoyé lorsqu'un pop-up est fermé. Vous avez peut-être également remarqué que si vous utilisiez la touche de tabulation, la fenêtre pop-up s'est fermée. C'est logique. Bien que les pop-ups permettent de gérer le ciblage, 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 pop-ups, il existe un modèle difficile à prendre en compte : ancrer l'élément à son déclencheur. C'est le cas, par exemple, si une info-bulle est configurée pour s'afficher au-dessus de son déclencheur, mais que l'utilisateur fait défiler le document. Cette info-bulle risque d'être tronquée par la fenêtre d'affichage. Il existe actuellement des solutions JavaScript pour gérer ce problème, telles que l'interface utilisateur flottante. Il repositionnera l'info-bulle pour que vous puissiez éviter que cela ne se produise et s'appuie sur l'ordre de position souhaité.

Mais nous voulons que vous puissiez définir cela avec vos styles. Pour résoudre ce problème, une API associée est en cours de développement en plus de l'API Popover. Positionnement de l'ancre CSS L'API vous permet de partager la connexion entre des éléments et d'autres éléments, en repositionnant les éléments de sorte qu'ils ne soient pas tronqués par la fenêtre d'affichage.

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

Voici un extrait du code CSS permettant de faire fonctionner cette démonstration. JavaScript n'est pas nécessaire.

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

Consultez les spécifications. Un polyfill est également disponible pour cette API.

Exemples

Maintenant que vous savez ce que le pop-over a à offrir et comment, examinons quelques exemples.

Notifications

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

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

Toasts

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

  • Un pop-over de type manual agit en tant que conteneur.
  • Les nouvelles notifications sont ajoutées au pop-up et celui-ci s'affiche.
  • Ils sont supprimés avec l'API d'animations Web lors d'un clic et supprimés du DOM.
  • Si aucun toast n'est affiché, le pop-up est masqué.

Menu imbriqué

Cette démonstration illustre le fonctionnement d'un menu de navigation imbriqué.

  • 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.
  • C'est un candidat idéal pour l'API CSS Anchoring. Toutefois, pour cette démonstration, vous pouvez utiliser un peu de JavaScript afin de 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 cette démonstration utilise autofocus. Vous devrez donc l'ouvrir en mode plein écran. pour la navigation au clavier.

Pop-over multimédia

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

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

Pop-overs de style wiki

Cette démonstration vous montre comment créer des info-bulles de contenu intégré 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é sur pointerenter avec JavaScript.
  • L'API CSS Anchoring est un autre candidat idéal.

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

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

Gérer les Backdrops

Cette démonstration vous montre comment gérer les arrière-plans pour les pop-ups multiples où vous souhaitez qu'une seule ::backdrop soit visible.

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

Pop-over personnalisé du curseur

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

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

Pop-over de la feuille d'actions

Cette démonstration vous montre comment utiliser un pop-up comme fiche d'action.

  • Affichez le pop-up par défaut en remplaçant display.
  • La feuille d'actions est ouverte avec le déclencheur contextuel.
  • Lorsque la fenêtre contextuelle s'affiche, elle est placée dans la couche supérieure et convertie en vue.
  • La fermeture légère peut être utilisée pour la renvoyer.

Pop-over 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 la fenêtre pop-up.
  • Le input est axé avec autofocus.
  • La boîte combinée est un deuxième popover placé sous l'entrée principale.
  • L'option "Opacité claire" ferme la palette si le menu déroulant n'est pas présent.
  • Autre candidat pour l'API Anchoring

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 une touche de fantaisie à votre site et ajouter un économiseur d'écran.

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

Suivi du accent circonflexe

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écessiterait une attention particulière vis-à-vis du contenu présenté et de son 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 le pop-up pour implémenter un menu de boutons d'action flottant sans JavaScript.

  • Faites passer un pop-up de type manual à l'aide de 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 est ouvert avec popovertoggletarget.
  • Utilisez autofocus pour sélectionner le premier élément de menu à afficher.
  • L'option "Fermer" permet de fermer le menu.
  • La rotation de l'icône utilise :has(). Pour en savoir plus sur :has(), consultez cet article.

Et voilà !

Nous allons vous présenter les pop-ups, qui seront bientôt disponibles dans le cadre de l'initiative Open UI. Utilisé de manière judicieuse, il constituera un atout formidable pour la plate-forme Web.

N'oubliez pas de consulter Ouvrir l'interface utilisateur. La fenêtre d'explication contextuelle est mise à jour à mesure que l'API évolue. Et voici la collection de toutes les démonstrations.

Merci pour votre participation !


Photo de Madison Oren sur Unsplash