Pourquoi le positionnement flottant est longtemps resté compliqué
Positionner une infobulle, un menu déroulant ou une palette de commandes semble simple au premier abord : on veut afficher un élément près d’un bouton. Pourtant, dans une vraie interface, les problèmes arrivent vite.
Le bouton peut être proche du bord de l’écran. Le conteneur parent peut scroller. Le texte peut changer de taille. Le zoom navigateur peut modifier les dimensions. Le menu peut devoir s’ouvrir au-dessus plutôt qu’en dessous. Et si l’on ajoute des contraintes d’accessibilité, de responsive design et de performance, un simple position: absolute devient rapidement insuffisant.
Pendant longtemps, la solution courante consistait à mesurer les éléments en JavaScript avec getBoundingClientRect(), puis à appliquer des coordonnées calculées dynamiquement. Des bibliothèques comme Floating UI ont rendu cette approche beaucoup plus robuste, mais le besoin de fond reste le même : le CSS devrait pouvoir exprimer qu’un élément dépend géométriquement d’un autre.
C’est précisément le rôle de CSS Anchor Positioning : permettre à un élément positionné de s’ancrer à un autre élément, directement dans les styles.
Ce sujet complète naturellement la Popover API, qui fournit un comportement natif pour afficher et masquer des panneaux flottants. La Popover API répond à la question “quand afficher le panneau ?”. CSS Anchor Positioning répond à la question “où le placer ?”.
Le principe : déclarer une ancre, puis s’y accrocher
CSS Anchor Positioning repose sur deux idées principales :
- un élément de référence reçoit un nom d’ancre ;
- un élément positionné utilise cette ancre pour calculer ses coordonnées.
Voici un exemple minimal avec un bouton et un panneau d’aide.
<button class="help-button">Aide</button>
<div class="help-panel">
Utilisez ce bouton pour ouvrir les options avancées du projet.
</div>
.help-button {
anchor-name: --help-button;
}
.help-panel {
position: absolute;
position-anchor: --help-button;
top: anchor(bottom);
left: anchor(left);
margin-top: 0.5rem;
max-width: 18rem;
padding: 0.75rem 1rem;
border: 1px solid #ddd;
border-radius: 0.75rem;
background: white;
box-shadow: 0 1rem 2rem rgb(0 0 0 / 0.12);
}
La propriété anchor-name déclare que le bouton peut servir de point de référence. La propriété position-anchor indique ensuite au panneau quel élément utiliser. Enfin, la fonction anchor() permet de récupérer les coordonnées de l’ancre : son bord haut, bas, gauche, droit ou son centre.
Le bénéfice est important : on décrit une relation spatiale au lieu de recalculer des pixels en JavaScript.
Utiliser position-area pour écrire moins de coordonnées
Pour de nombreux cas courants, on ne veut pas forcément définir séparément top, right, bottom et left. On veut simplement dire : “place ce menu sous le bouton, aligné à gauche”, ou “place cette bulle au-dessus, centrée”.
CSS Anchor Positioning introduit pour cela position-area.
.menu-button {
anchor-name: --account-menu;
}
.account-menu {
position: absolute;
position-anchor: --account-menu;
position-area: bottom start;
margin-top: 0.5rem;
min-width: 14rem;
}
Cette syntaxe rend le code plus lisible. bottom start signifie que l’élément flottant doit être placé dans la zone située sous l’ancre, alignée sur le début de l’axe d’écriture. En français ou en anglais, dans une interface de gauche à droite, cela correspond généralement à un alignement à gauche. Dans une interface RTL, l’interprétation peut changer correctement selon le mode d’écriture.
C’est un détail important pour l’internationalisation : préférer start et end à left et right permet de produire des interfaces plus adaptables.
Combiner Anchor Positioning avec la Popover API
CSS Anchor Positioning devient particulièrement intéressant avec les popovers natifs. Le HTML peut gérer l’ouverture et la fermeture, tandis que le CSS gère le placement.
<button popovertarget="settings-popover" class="settings-button">
Paramètres
</button>
<div id="settings-popover" popover class="settings-popover">
<button>Profil</button>
<button>Facturation</button>
<button>Déconnexion</button>
</div>
.settings-button {
anchor-name: --settings-button;
}
.settings-popover {
position-anchor: --settings-button;
position-area: bottom end;
margin-top: 0.5rem;
padding: 0.5rem;
border: 1px solid var(--color-border, #ddd);
border-radius: 0.75rem;
background: var(--color-surface, white);
box-shadow: 0 1rem 2rem rgb(0 0 0 / 0.15);
}
.settings-popover button {
display: block;
width: 100%;
padding: 0.5rem 0.75rem;
border: 0;
background: transparent;
text-align: start;
}
Le résultat est plus déclaratif qu’une solution entièrement pilotée par JavaScript. Le navigateur connaît déjà la géométrie des éléments, les contraintes de viewport et les modes d’écriture. Lui déléguer le placement est donc souvent plus sain.
Pour une interface cohérente, on peut aussi combiner cette approche avec des design tokens en CSS et TypeScript, par exemple pour standardiser les espacements, rayons, ombres et couleurs des éléments flottants.
Gérer les collisions avec le viewport
Le vrai problème des menus et tooltips n’est pas seulement de les placer. C’est de les placer correctement quand il n’y a pas assez d’espace.
Un menu sous un bouton peut sortir de l’écran si le bouton est trop bas. Un tooltip à droite peut être coupé si l’élément est proche du bord droit. CSS Anchor Positioning prévoit des mécanismes de fallback avec position-try et position-try-fallbacks.
.tooltip-trigger {
anchor-name: --tooltip-trigger;
}
.tooltip {
position: absolute;
position-anchor: --tooltip-trigger;
position-area: top;
margin-bottom: 0.5rem;
position-try-fallbacks: bottom, right, left;
}
Dans cet exemple, le tooltip essaie d’abord de se placer au-dessus de son ancre. Si cette position ne convient pas, le navigateur peut essayer en dessous, puis à droite, puis à gauche.
Cela ne remplace pas tous les cas avancés d’une bibliothèque spécialisée, mais cela couvre une grande partie des besoins d’interface classiques : menus, bulles d’aide, panneaux contextuels, raccourcis, badges et petites cartes de prévisualisation.
Exemple TypeScript : activer une classe selon le support navigateur
Comme toute API CSS récente, Anchor Positioning doit être introduit avec une stratégie de compatibilité. Une bonne pratique consiste à détecter le support avec [CSS](/articles/css-style-page-web).supports() et à prévoir une mise en page de secours.
const supportsAnchorPositioning = CSS.supports('anchor-name: --test')
document.documentElement.classList.toggle(
'supports-anchor-positioning',
supportsAnchorPositioning
)
.floating-card {
position: absolute;
top: 100%;
left: 0;
margin-top: 0.5rem;
}
.supports-anchor-positioning .card-trigger {
anchor-name: --card-trigger;
}
.supports-anchor-positioning .floating-card {
position-anchor: --card-trigger;
position-area: bottom start;
}
Ici, le fallback reste simple : le panneau est positionné relativement à un conteneur classique. Si Anchor Positioning est disponible, on active un placement plus précis.
Cette logique s’inscrit dans une démarche de progressive enhancement : l’interface doit rester utilisable sans dépendre entièrement de la fonctionnalité la plus récente.
Architecture CSS : éviter les ancres dispersées partout
Comme pour toutes les fonctionnalités puissantes, le danger est de l’utiliser sans convention. Si chaque composant invente ses propres noms d’ancres et ses propres règles de placement, l’architecture devient vite difficile à maintenir.
Une convention simple consiste à nommer les ancres selon le composant ou le rôle UI.
.user-menu__trigger {
anchor-name: --user-menu-trigger;
}
.user-menu__panel {
position-anchor: --user-menu-trigger;
position-area: bottom end;
}
Pour des composants réutilisables, on peut aussi exposer le placement sous forme de variantes CSS.
.floating-panel {
position: absolute;
position-anchor: var(--anchor);
}
.floating-panel[data-placement='bottom-start'] {
position-area: bottom start;
}
.floating-panel[data-placement='bottom-end'] {
position-area: bottom end;
}
.floating-panel[data-placement='top'] {
position-area: top;
}
Cette approche se combine bien avec une organisation en couches via les CSS cascade layers. Les styles structurels du composant peuvent vivre dans une couche components, tandis que les ajustements applicatifs restent dans une couche de surcharge contrôlée.
Accessibilité : le placement ne suffit pas
Anchor Positioning résout un problème visuel, pas un problème d’interaction. Un menu bien placé peut rester inaccessible s’il ne respecte pas les attentes clavier, focus et lecteur d’écran.
Pour un simple panneau d’information, il faut vérifier que le contenu est lisible, que le contraste est suffisant et que l’apparition ne masque pas une information essentielle. Pour un menu interactif, il faut gérer correctement le focus, la fermeture avec Échap, l’ordre de tabulation et la sémantique.
La Popover API aide déjà sur certains points, mais elle ne transforme pas automatiquement n’importe quel bloc en composant accessible parfait. Pour des formulaires, messages d’erreur ou retours dynamiques, les principes décrits dans les formulaires accessibles et les ARIA live regions restent indispensables.
Autre point souvent oublié : les animations. Un panneau flottant peut apparaître avec une transition légère, mais il faut respecter prefers-reduced-motion.
.floating-panel {
opacity: 0;
transform: translateY(-0.25rem);
transition: opacity 160ms ease, transform 160ms ease;
}
.floating-panel:popover-open {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.floating-panel {
transition: none;
transform: none;
}
}
Quand utiliser CSS Anchor Positioning ?
CSS Anchor Positioning est particulièrement adapté quand un élément flottant dépend visuellement d’un déclencheur clair : bouton, champ de formulaire, icône d’aide, élément sélectionné ou carte.
Il est pertinent pour :
- les menus de navigation ou de compte ;
- les tooltips et infobulles ;
- les popovers d’aide ;
- les panneaux de filtres ;
- les petites cartes de prévisualisation ;
- les interfaces d’administration riches.
Il est moins adapté si votre composant exige une logique métier complexe, une détection fine des collisions, des animations très contrôlées ou une compatibilité stricte avec d’anciens navigateurs. Dans ces cas, une bibliothèque spécialisée peut rester justifiée.
Mais pour de nombreuses interfaces modernes, cette API réduit la quantité de JavaScript nécessaire, clarifie la responsabilité du CSS et rend le code plus déclaratif.
Conclusion
CSS Anchor Positioning marque une évolution importante du CSS moderne : au lieu de bricoler des coordonnées à la main, on peut exprimer directement une relation entre un élément flottant et son élément de référence.
Combinée à la Popover API, aux design tokens, aux cascade layers et à une stratégie de progressive enhancement, cette fonctionnalité permet de construire des composants UI plus simples, plus lisibles et plus maintenables.
Ce n’est pas une raison pour ignorer l’accessibilité ou les fallbacks. Mais c’est une excellente raison de réévaluer les vieux morceaux de JavaScript qui ne servent qu’à déplacer un menu de quelques pixels.