Lorsque vous créez des info-bulles ou des popovers avec le positionnement d'ancrage CSS, le navigateur peut automatiquement "basculer" l'élément vers une position de remplacement s'il dépasse de la fenêtre d'affichage. Dans ce cas, vous pouvez ajuster le style du contenu positionné, par exemple pour repositionner une flèche qui pointe du contenu positionné vers l'ancre.
Les requêtes de conteneur ancrées résolvent ce problème en vous permettant d'interroger l'état de positionnement actif d'un élément et d'appliquer des styles en conséquence. Pour en savoir plus sur le positionnement général des menus déroulants et des menus contextuels ancrés avec retournement en cas de collision avec la fenêtre d'affichage (sans modification du style de la flèche directionnelle), consultez Menus déroulants adaptés à la fenêtre d'affichage.
Problème
Imaginez une info-bulle qui s'affiche par défaut au-dessus de son ancrage. Il comporte une flèche vers le bas en bas. Si l'utilisateur fait défiler la page et que l'info-bulle s'affiche en dessous de l'ancrage, la flèche pointe désormais dans la mauvaise direction et se trouve du mauvais côté de l'info-bulle.
La solution : les requêtes de conteneur ancrées
En définissant container-type: anchored sur votre élément positionné, vous le transformez en conteneur de requête qui connaît son propre état de positionnement d'ancrage. Vous pouvez ensuite utiliser la requête @container anchored() pour mettre à jour ses descendants ou ses pseudo-éléments.
1. Créer l'info-bulle et le déclencheur
Utilisez l'API Popover pour créer une info-bulle. L'appel du popover via popovertarget établit automatiquement une connexion d'ancrage implicite avec le bouton de déclenchement. Aucun anchor-name ou position-anchor explicite n'est donc requis.
<button popovertarget="tooltip" id="anchor" aria-describedby="tooltip">anchor</button>
<div id="tooltip" popover role="tooltip"></div>
Réinitialisez les styles d'encart et de marge du popover pour les utiliser avec le positionnement d'ancre, mais uniquement si le positionnement d'ancre est pris en charge.
@supports (anchor-name: --my-anchor) {
[popover] {
inset: auto;
margin: unset;
}
}
2. Configurer le conteneur
Appliquez container-type: anchored à l'élément en cours de positionnement. Cet élément doit également avoir défini position-try-fallbacks pour activer le comportement de retournement.
#tooltip {
position: fixed;
position-area: block-start;
position-try-fallbacks: flip-block;
/* Enable anchored container queries */
container-type: anchored;
}
3. Style basé sur le style de remplacement
Utilisez @container anchored(fallback: <value>) pour appliquer des styles lorsqu'un remplacement spécifique est actif.
Comme toutes les requêtes de conteneur, @container ne peut appliquer de style qu'aux descendants du conteneur. Une stratégie courante pour créer les flèches consiste à utiliser les pseudo-éléments ::before et ::after, qui sont traités comme des descendants et peuvent être stylisés directement. Toutefois, pour styliser l'info-bulle elle-même (comme indiqué à l'étape 4), nous allons ajouter un élément enfant à l'info-bulle et créer la flèche dans son pseudo-élément ::before.
<div id="tooltip" popover role="tooltip">
<div class="tooltip-content">Tooltip</div>
</div>
.tooltip-content::before {
/* Default "down" arrow for the 'top' position */
content: "▼";
position: absolute;
inset-block-end: 0;
inset-inline-start: 1rem;
}
/* Update to an "up" arrow when the 'flip-block' fallback (bottom) is active */
@container anchored(fallback: flip-block) {
.tooltip-content::before {
content: "▲";
inset-block-start: 0;
inset-block-end: auto;
}
}
4. Styliser le conteneur lui-même
Si vous devez modifier les propriétés du conteneur lui-même (comme margin ou background-color) lorsqu'il est retourné, vous devez utiliser un élément wrapper interne.
- Appliquez
container-type: anchoredà l'élément positionné externe. - Ciblez l'élément interne dans le bloc
@container.
@container anchored(fallback: flip-block) {
.tooltip-content {
border-radius: 0 0 .5rem .5rem;
margin-block-start: 0.25rem;
}
}
Bonnes pratiques
- Privilégiez les solutions de remplacement logiques : utilisez des mots clés tels que
flip-blocketflip-inlinedansposition-try-fallbackspour des requêtes plus simples qui gèrent automatiquement l'écriture de droite à gauche et les différents modes d'écriture. - Utilisez des pseudo-éléments pour les flèches : les flèches des info-bulles sont purement décoratives et sont parfaites pour
::beforeou::after, qui peuvent être stylisées via des requêtes de conteneur ancrées sans DOM supplémentaire.
Stratégies de remplacement
Le positionnement de la flèche en fonction du remplacement appliqué est une amélioration progressive, et il n'existe pas d'autre moyen de réagir à la position de remplacement. Pour masquer la flèche dans les navigateurs qui ne sont pas compatibles avec les requêtes de conteneur de position d'ancrage, testez la compatibilité CSS avec @supports (container-type: anchored).
@supports (container-type: anchored) {
.tooltip-content::before {
content: "▼";
}
}
Solutions de remplacement et compatibilité des navigateurs pour Popover
L'API Popover est principalement une amélioration progressive, mais ses comportements déterminants (promotion de la couche supérieure, fermeture par clic en dehors de l'élément et appel de popovertarget) n'ont pas d'équivalent CSS uniquement. Les anciens navigateurs ont besoin d'un polyfill ou d'un remplacement manuel si vous préférez ne pas en fournir.
Polyfill : pour que l'attribut popover soit compatible avec les anciens navigateurs, chargez @oddbird/popover-polyfill de manière conditionnelle. OBLIGATOIRE : Détectez la fonctionnalité en vérifiant la propriété popover sur HTMLElement.prototype, et chargez le polyfill uniquement lorsque la compatibilité native est manquante. Ne le chargez PAS de manière inconditionnelle.
Avec un bundler ou une carte d'importation :
// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
import("@oddbird/popover-polyfill");
}
Sans bundler, importez depuis un CDN dans un <script type="module"> :
<script type="module">
if (!("popover" in HTMLElement.prototype)) {
import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
}
</script>
Mise en garde concernant l'application de styles : le polyfill ne peut pas définir la véritable pseudo-classe :popover-open. Il applique donc une classe .\:popover-open à la place. OBLIGATOIRE : Combinez les deux avec :is() ou :where(), sinon les navigateurs qui ne disposent pas de :popover-open ignorent l'intégralité de la règle :
[popover]:is(:popover-open, .\:popover-open) {
display: block;
}
Vous pouvez également utiliser position: fixed pour une solution de remplacement sans polyfill et calculer manuellement les coordonnées via getBoundingClientRect(), ou vous appuyer sur le positionnement par défaut avec inset: auto si cela convient à votre cas d'utilisation.
Les navigateurs qui ne sont pas compatibles avec l'API Popover ne sont pas non plus compatibles avec le positionnement des ancres. L'info-bulle s'affiche donc au centre de l'écran.