Créer un menu déroulant, une modale ou un tooltip a longtemps été synonyme de nombreuses lignes de code JavaScript, de bibliothèques tierces, et de bugs d’accessibilité ou de responsive.
Deux nouvelles technologies natives aux navigateurs changent maintenant la donne : Popover API et CSS Anchor Positioning. Cette API HTML et ce module CSS forment ensemble une solution complète pour construire des interfaces modernes : plus légères, plus accessibles et bien moins dépendantes des frameworks.
Passons ensemble en revue ce qu’elles font, comment les utiliser et pourquoi elles marquent un vrai tournant pour le développement front-end.
Les interfaces flottantes, ou floating UI, sont des blocs apparaissant temporairement par-dessus le contenu d’un site internet. Ces interfaces peuvent prendre la forme de :
- tooltip : un petit encart flottant apparaissant au clic ou survol d’un texte ou d’une icône, présentant une explication supplémentaire
- modale : aussi appelée popup, c’est un encart qui apparaît par-dessus la page, bloquant le reste de la navigation
- menu déroulant : des menus secondaires apparaissant après un clic ou un survol d’un autre élément de navigation.
Contrairement à un élément statique comme un paragraphe, un tableau ou un média, ces éléments ne respectent pas le flux naturel de la page. Ils doivent apparaître par-dessus, s’aligner avec des éléments cibles ou sur l’entièreté de l’écran, disparaître à des moments calculés et précis… Ce sont autant de contraintes qui se cumulent et nécessitent de nombreux ajustements dans le code.
Jusqu’à fin janvier 2026, la solution a été quasi exclusivement du JavaScript. Positionner un sous-menu sous un menu parent pouvait se faire en CSS natif, mais écouter les déclencheurs, les événements de scroll, de redimensionnement de la page, et recalculer l’ensemble en permanence, cela demandait obligatoirement du JavaScript.
Ce développement était consommateur de ressources pour la page, donc l’alourdissait en conséquence, impactant fortement les performances. De plus, il pouvait également s’avérer fragile avec des effets de bord souvent difficiles à débugger.
Des bibliothèques sont nées pour aider les développeurs à automatiser et éviter la complexité de ces développements, comme Popper.js ou Floating UI. Ce sont des librairies efficaces, mais qui impliquent donc une dépendance supplémentaire pour le site, un JavaScript plus lourd à charger, et peuvent avoir des lacunes notamment en accessibilité.
Les solutions développées manuellement souffrent principalement de trois problèmes non négligeables.
Le WCAG 2.1, référentiel international de règles d’accessibilité numérique, et son équivalent français le RGAA, imposent qu’un composant flottant expose son état ouvert/fermé, permette la navigation au clavier, et gère correctement le focus ainsi que l’interaction avec les lecteurs d’écran.
Ces comportements sont rarement implémentés correctement dans les solutions custom, voire même dans les bibliothèques toutes faites.
Comme dit précédemment, à chaque scroll ou redimensionnement de la fenêtre, les solutions en JavaScript, doivent réécouter l’événement, recalculer la position de l’élément, puis l’appliquer manuellement, plusieurs fois par seconde. Cet aller-retour constant entre écoute et écriture sollicite fortement le navigateur, avec un impact d’autant plus visible que l’appareil est peu puissant.
Chaque composant développé manuellement devient une source de maintenance supplémentaire à chaque évolution du site. Impact sur le nouveau code, régression avec les évolutions des plugins… le JavaScript peut vite être très chronophage à débugger et avoir des impacts divers et variés.
Un popover natif, utilisant la Popover API, est un élément natif du navigateur, permettant de créer des éléments flottants interactifs directement en HTML, sans dépendance à une bibliothèque ni à une quelconque logique Javascript. En ajoutant l’attribut popover à n’importe quel élément, il va devenir un élément affiché en overlay (au-dessus du reste de la page) avec par défaut les possibilités suivantes :
- fermeture au clic à l’extérieur de l’élément
- fermeture via la touche Échap,
- gestion native du focus
- gestion native au clavier
<button>Ouvrir</button>
<div id="mon-popover">Contenu du popover</div>Le navigateur prend donc maintenant en charge ce qui demandait auparavant du code sur mesure.
C’est là tout l’intérêt et la valeur ajoutée de cette API : des comportements qui nécessitaient auparavant de nombreuses lignes de JavaScript sont désormais gérés nativement, via quelques attributs.
Le popover natif a 3 états distincts :
FERMÉ
L'élément est totalement masqué
OUVERT
L'élément est visible et placé dans le top layer, soit au-dessus de tout le reste de la page, y compris des éléments avec un z-index élevé
EN TRANSITION
Permet d'animer l'apparition et la disparition de l'élément, sans avoir recours à des astuces CSS pour simuler des transitions sur un élément qui n'est pas préchargé initialement
L’attribut popover accepte 2 valeurs principales :
auto: active la fermeture automatique au clic extérieur ou à la touche Échap. C’est le comportement standard d’une modale. Un popover en mode auto se ferme également en cascade si un autre popover s’ouvre à proximité, un comportement notamment attendu pour les menus ayant plusieurs sous-menus.manual: laisse le contrôle entier au développeur via JavaScript
Le mode manuel laisse donc l’opportunité d’une gestion plus fine en utilisant des fonctions JavaScript disponibles :
const popover = document.querySelector('#mon-popover');
popover.showPopover();
popover.hidePopover();
popover.togglePopover();Ainsi, contrairement à une popup classique, qui capture le focus et bloque toute interaction avec le reste de l’interface jusqu’à fermeture via un clic sur un bouton, un popover en mode auto reste non-bloquant : l’utilisateur peut interagir ailleurs, et la fermeture se fait implicitement.
De plus, contrairement aux popovers traditionnels, construits grâce à des position: absolute, des z-index savamment calculés et des gestionnaires d’événements pour détecter les clics extérieurs, la Popover API gère cet empilement visuel et le cycle de fermeture directement dans le navigateur, sans code applicatif dédié.
Le navigateur gère aussi nativement plusieurs comportements essentiels pour l’accessibilité.
Lorsque la relation est clairement établie entre un popover et son élément de contrôle (via l’attribut popovertarget), l’API apporte automatiquement deux autres modifications afin de permettre aux utilisateurs clavier et de technologies d’assistance (ex: lecteur d’écran) d’interagir plus facilement avec le popover :
- lors de l’affichage du popover, l’ordre de navigation au clavier est mis à jour pour que le popover soit le prochain élément du focus :
- par exemple, si un bouton est activé pour afficher un popover, la navigation via la tabulation parcourra ensuite tous les boutons situés à l’intérieur de celui-ci (ils seront mis en focus en appuyant sur la touche Tab).
- À l’inverse, lorsque le popover est fermé à l’aide du clavier (généralement via la touche Échap), le focus revient au bouton déclencheur de son ouverture.
Pour que les lecteurs d’écran et autres technologies d’assistance puissent faire le lien entre l’élément d’appel et la fenêtre contextuelle dans l’application web, Popover API gère automatiquement une relation aria-details et aria-expanded entre eux.
Cela dit, la Popover API ne gère pas totalement tous les attributs ARIA. Afin de respecter le RGAA, selon la nature du composant en popover, il reste important de préciser son rôle sémantique pour les technologies d’assistance. Un tooltip, un menu et une fenêtre de confirmation n’ont pas le même rôle fonctionnel et les lecteurs d’écran ont besoin de cette information pour restituer correctement l’interface.
<div id="mon-popover" role="dialog" aria-label="Paramètres">...</div>Le fait que Popover API facilite l’implémentation des popover ne doit pas pour autant encourager l’abus de leur utilisation. Un popover mal pensé dégrade l’expérience utilisateur et peut avoir un impact négatif sur l’application web.
Pour en savoir plus, lisez notre article sur les bonnes pratiques d’utilisation des popup :
Le CSS permet déjà de positionner un élément par rapport à un autre grâces aux propriétés de position relative et absolute. Néanmoins, cela impose une contrainte structurelle dans le DOM : l’élément à positionner doit être un descendant de l’élément qui lui sert de référence.
Cela peut correspondre à un menu déroulant : le menu enfant est un descendant du menu parent, via par exemple cette hiérarchie HTML :
<ul>
<li>Menu A</li>
<li>Menu B parent</li>
<ul>
<li>Menu enfant 1</li>
<li>Menu enfant 2</li>
<li>Menu enfant 3</li>
</ul>
<li>Menu C</li>
</ul>Pour pouvoir ancrer un popover à un bouton situé à un autre emplacement dans le DOM, cela implique donc :
- soit de réorganiser le balisage HTML
- soit, si l’élément n’a pas à bouger dans le viewport, de recourir à la propriété CSS
position: fixed;, couplé à un calcul CSS ou JavaScript pour sa position - soit de calculer en JavaScript sa position, avec un recalcul automatique à chaque scroll ou redimensionnement du viewport
C’est donc toute cette contrainte de structure, et le calcul JavaScript qu’elle impose quand il n’y en a pas, que CSS Anchor Positioning vient lever. Ce module CSS permet de positionner un élément par rapport à un autre sans lien de parenté dans le DOM, en laissant le navigateur calculer nativement leur position relative l’un à l’autre.
L’anchor positioning repose sur un principe simple : déclarer un élément comme point de référence (l’ancre), puis positionner un autre élément par rapport à lui, directement en CSS.
Pour ce faire, il suffit de désigner en CSS
- l’ancre en lui donnant un nom avec
anchor-name - l’élément flottant avec
position-anchor
Les deux éléments n’ont aucune contrainte d’imbrication dans le DOM : un popover peut ainsi être ancré à un bouton situé complètement ailleurs dans la structure HTML de la page, ce qui simplifie considérablement l’organisation du balisage dans une application web.
Le navigateur calcule et maintient ce positionnement automatiquement, sans JavaScript, et sans recalcul manuel à chaque scroll ou redimensionnement.
/* L'élément déclencheur devient une ancre */
#bouton {
anchor-name: --mon-bouton;
}
/* Le popover se positionne par rapport à cette ancre */
#popover {
position: absolute;
position-anchor: --mon-bouton;
top: anchor(bottom);
left: anchor(left);
}C’est là toute la puissance de cette nouvelle fonctionnalité CSS. Pour bien comprendre, il faut visualiser : un menu dans un site web a un menu enfant. Ce menu est tout à droite de la page et son menu enfant s’affiche centré sous lui. Lorsque le menu a de l’espace autour de lui ça ne pose aucun souci, mais en réduisant la fenêtre, le sous-menu se trouve d’abord collé au bord de la fenêtre puis
- soit il provoque un scroll horizontal dans la page en repoussant le bord du contenu plus loin que la largeur classique de la page
- soit une partie du sous-menu se retrouve masquée par le bord de la fenêtre du navigateur
CSS Anchor Positioning prévoit une propriété CSS particulière pour gérer ces situations, permettant de définir des positions alternatives que le navigateur va appliquer automatiquement lorsque la position par défaut n’est plus possible. Ce paramètre est position-try-fallbacks, ou son raccourci position-try.
#popover {
position-anchor: --mon-bouton;
top: anchor(bottom);
left: anchor(left);
position-try-fallbacks: flip-block, flip-inline;
}Explication de l’exemple ci-contre :
- si le popover dépasse en bas de l’écran, le navigateur tente de le placer au-dessus du bouton
- s’il dépasse sur le côté, il tente l’inversion horizontale.
Le navigateur teste dans l’ordre les options jusqu’à trouver celle qui garde l’élément entièrement visible à l’écran.
Ce comportement était auparavant l’une des fonctionnalités les plus complexes à reproduire en JavaScript et nécessitait de nombreuses lignes et des calculs constants du navigateur. Aujourd’hui il devient une simple déclaration CSS, sans code applicatif nécessaire, et surtout sans le coût de performance associé.
Chacune de ces deux technologies résout donc une partie du problème des éléments flottants :
- La Popover API gère le comportement, soit le « quand » et le « comment » : ouverture, fermeture, focus, interactions clavier.
- CSS Anchor Positioning gère le positionnement, soit le « où » : placement précis, alignement, adaptation au viewport.
Cette complémentarité se retrouve dans la quasi-totalité des interfaces flottantes d’une application web, par exemple :
- un menu déroulant a besoin d’un comportement de type popover (fermeture au clic extérieur, gestion du focus) et d’un positionnement ancré à son bouton déclencheur,
- un tooltip a besoin d’un ancrage précis à l’élément survolé, qui va s’adapter en fonction de sa position dans la fenêtre ou dans son bloc parent,
Dans les deux cas, le même couple popover + anchor suffit — seule la valeur de l’attribut popover et la stratégie de repositionnement changent. Le fait de les combiner permet donc de couvrir l’intégralité des besoins d’un composant flottant, sans avoir besoin d’appeler une bibliothèque tierce, sans calcul JavaScript, et avec une accessibilité intégrée dès le départ.
<button id="bouton" popovertarget="popover">Ouvrir</button>
<div id="popover" popover role="dialog" aria-label="Menu contextuel">
Contenu
</div>#bouton {
anchor-name: --bouton-ancre;
}
#popover {
position: absolute;
position-anchor: --bouton-ancre;
top: anchor(bottom);
left: anchor(left);
position-try-fallbacks: flip-block, flip-inline;
}Séparer les solutions à ces deux problématiques en deux standards natifs distincts, l’un HTML, l’autre CSS, permet à chaque brique de rester simple et de bien faire une seule chose. Car il faut garder à l’esprit que si ces deux technologies sont distinctes, c’est aussi pour garder cette autonomie de gestion. Un popover peut fonctionner sans avoir besoin d’être ancré (centré dans la fenêtre par exemple), et un élément peut servir d’ancre à autre chose qu’un popover.
En cas d’évolution des besoins, comme changer le mode de fermeture ou ajouter une nouvelle stratégie de fallback, chaque couche peut être modifiée séparément, sans toucher à l’autre.
C’est là toute la force de cette combinaison en améliorant considérablement l’expérience utilisateur. Avec des bibliothèques tierces ou du code JavaScript maison, chaque scroll, chaque redimensionnement, et parfois même chaque action, déclenche un cycle complet qui mobilise les ressources du navigateur à chaque instant :
- écoute de l’événement,
- lecture des données transmises,
- calcul de la nouvelle position, du nouveau comportement,
- écriture dans le DOM.
Avec le couple Popover API + CSS Anchor Positioning, ce cycle disparaît de l’application web : le popover est extrait du flux normal comme auparavant (position: absolute;), mais son repositionnement est calculé nativement par le moteur de rendu du navigateur, au même titre qu’un position: sticky; ou qu’une transition CSS.
Même dans le cas où plusieurs popovers sont présents (menus imbriqués, tooltips multiples, etc.), le nettoyage des écouteurs d’événement se fait nativement par le navigateur : à la fermeture, les écouteurs internes d’un popover et son état sont automatiquement libérés, sans code de démontage à écrire et maintenir. Le mode auto, qui permet un comportement de fermeture en cascade, réduit également le nombre d’instances actives simultanément. Par ailleurs, le popover natif profite du même mécanisme d’affichage conditionnel que display: none; : dans son état fermé, son contenu n’est pas rendu à l’écran, et son coût pour le navigateur reste donc minimal.
En navigation mobile notamment, ou du moins sur des appareils aux ressources limitées, le changement est flagrant : là où le JS pouvait provoquer des saccades visibles au scroll, tout redevient fluide.
Dans une application web, les menus déroulants sont les éléments les plus courants : dans la barre de navigation, un bouton « utilisateur » regroupant plusieurs actions possibles…
Là où avant plusieurs lignes de JS étaient nécessaires pour gérer toutes les options d’ouverture, fermeture, de sous-sous-menu, etc., avec Popover + Anchor, le développement se résume en 3 propriétés :
- le bouton reçoit
anchor-name, - le menu est déclaré avec
popover=“auto” - il est ancré avec
position-anchor
Et c’est tout. Le comportement de fermeture (clic extérieur, touche Échap, clic sur un autre menu) est géré automatiquement, tout comme le repositionnement si le menu est déclenché trop près du bord de l’écran.
Le tooltip (ou info-bulle en français) a des contraintes différentes : il doit rester lisible quel que soit l’endroit où se trouve l’élément qu’il décrit ou l’icône qui informe de sa présence. Il ne doit pas dépasser de la fenêtre, parfois même du bloc où il est présent (par exemple dans un tableau).
Les stratégies de fallback sont donc particulièrement importantes pour ces éléments : le tooltip doit pouvoir passer d’un affichage en haut à droite à en bas à gauche suivant l’espace réellement disponible pour lui. Ce comportement qui demandait auparavant un calcul de collision côté JavaScript se trouve simplifié en une seule propriété CSS.
Il existe des cas plus complexes nécessitant des popovers imbriqués :
- des menus à plusieurs niveaux de sous-menus
- des popups imbriquées (niveaux de filtres imbriqués par exemple)
Dans ce cas, l’ouverture d’un bloc enfant garde son parent ouvert, tandis que le clic à l’extérieur de l’ensemble referme tous les blocs en cascade.
Chaque niveau a sa propre ancre (ou absence d’ancre si c’est une modale centrée dans la fenêtre) et sa propre stratégie de repositionnement, ce qui permet par exemple à un sous-menu de basculer à droite ou à gauche de son sous-menu parent si l’espace vient à manquer.
Sur nos projets WordPress, le menu principal doit se comporter de deux façons différentes en fonction du responsive :
- une barre d’icônes fixée en bas de l’écran sur mobile, avec des sous-menus qui s’affichent au-dessus en volet et une navigation en profondeur (drill-down) en volets successifs,
- des menus déroulants classiques en cascade sur desktop.
L’approche habituelle consiste à construire deux menus séparés (un pour mobile, un pour ordinateur), en appelant deux fois wp_nav_menu(), et à masquer celui dont on n’a pas besoin en fonction de la taille du viewport. Cela implique donc :
- du contenu dupliqué dans le DOM
- deux styles CSS à maintenir
- du JavaScript pour piloter l’ensemble : l’ouverture, la fermeture au clic extérieur, la touche Échap et les états ARIA.
C’est beaucoup de code pour un composant que tout le monde considère comme acquis.
Popover API et CSS Anchor Positioning nous permettent de nous en passer entièrement. Le menu est rendu une seule fois dans l’application web et toute la différence de comportement se joue en CSS uniquement.
Pour ce faire, nous avons développé un Walker_Nav_Menu personnalisé qui fait trois choses :
- tout item ayant des enfants est transformé en
<button>porteur d’unpopovertarget, au lieu d’un lien classique<a>, étant donné qu’il ne mène nulle part : c’est un déclencheur, - chaque sous-menu
<ul>reçoit l’attributpopover, peu importe son niveau de profondeur, ce qui nous donne des popovers imbriqués, - chaque paire bouton/sous-menu est reliée par un identifiant d’ancre unique, dérivé de l’ID du menu item
Le point d’attention est le suivant : dès qu’on imbrique les popovers, la relation popovertarget produit des positionnements erronés si l’ancre n’est pas nommée des deux côtés, pour que chaque sous-menu sache précisément à quel bouton s’accrocher.
<button popovertarget="submenu-42"
style="anchor-name: --submenu-anchor-42"
>
Nos services
</button>
<ul id="submenu-42"
popover
style="position-anchor: --submenu-anchor-42"
>
…
</ul>Côté CSS, sur mobile on ignore complètement l’ancrage : le popover est simplement fixé au-dessus de la barre de navigation, sur toute la largeur de l’application. Ce n’est qu’à partir des écrans plus grands (desktop) qu’on active le positionnement avec l’ancre, avec un comportement spécifique à chaque niveau : le premier sous-menu s’ouvre sous son bouton, les suivants se déploient sur le côté. Lorsqu’un menu risquerait de sortir de l’écran, le navigateur le place du bon côté : c’est une simple ligne de CSS, là où il fallait autrefois mesurer la fenêtre en JavaScript.
.sub-menu[popover] {
inset: auto;
top: anchor(bottom);
right: anchor(right);
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
.sub-menu[popover] {
top: anchor(top);
right: anchor(left);
position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
}
}Le reste des états suit la même logique, les combinaisons :popover-open et :has() remplaçant les classes que le JavaScript ajoutait et retirait :
- la flèche pivote via
:has(> .sub-menu:popover-open), - en mobile, le fait de masquer les items voisins quand un sous-niveau s’ouvre tient en un sélecteur,
- les transitions d’ouverture s’appuient sur
transition-behavior: allow-discrete, qui permet enfin d’animerdisplayet la couche overlay
Au final, ce menu ne contient plus aucune ligne de JavaScript. L’ouverture, la fermeture au clic extérieur, l’empilement des popovers imbriqués, la touche Échap et le parcours au clavier sont assurés par le navigateur nativement et sans script à charger.
Ce qui demandait auparavant une bibliothèque tierce, des dizaines de lignes de JavaScript et des calculs permanents de position coûteux en performances, tient aujourd’hui en quelques attributs HTML et quelques propriétés CSS grâce à Popover API et CSS Anchor Positioning.
Ces deux fonctionnalités natives représentent une solution aboutie pour construire menus, popups et info-bulles plus légers, plus accessibles et moins coûteux à maintenir. Prises en charge par l’ensemble des navigateurs modernes depuis fin janvier 2026, elles permettent au navigateur de prendre maintenant en charge nativement ce qui relevait du code applicatif.
Chez Imagile, nous intégrons ces standards natifs lorsqu’ils apportent une réelle valeur ajoutée à la qualité et à la pérennité du site. Nous accompagnons chaque client dans l’analyse de ses besoins d’interface afin de déterminer les composants qui gagnent à être repensés de cette façon, sur un site ou une application existante, comme dans le cadre d’une refonte, ou dès la conception d’un nouveau projet lorsque celui-ci présente des exigences fortes en matière d’accessibilité, de conformité au RGAA, de performance ou d’écoconception.