CSS : Anchor positioning
Introduction Introduction
Nouveau module CSS de positionnement d’ancrage qui permet d’attacher des éléments d’ensemble à des éléments d’ancrage. On peut alors définir la taille et la position en fonction de la taille et de l’emplacement de l’élément d’ancrage.
De plus, on peut spécifier un ensemble de positions alternatives dès lors que l’élément ancré déborde du bloc conteneur ou de l’écran. Cela permet au navigateur de toujours l’afficher de manière optimale.
Avant tout cela n’était pas possible sans JavaScript.
Compatibilité
A l’heure actuelle, le module Anchor positioning n’est pas encore supporté par tous les navigateurs. N’hésitez pas à consulter la compatibilité sur Caniuse.
Cas d'usage Cas d'usage
Voici quelques exemples de cas d’usage courant pour le module Anchor positioning :
- Message d’erreur s’affiche à côté des contrôles de formulaire
- Tooltip qui s’affiche à côté d’un élément pour fournir des informations complémentaires
- Menu déroulant ou contextuel qui s’affiche à côté de l’élément qui l’a déclenché
- Boîte de dialogue de paramètres ou d’options pour de la configuration
- …
Ancrage Ancrage
Pour associer un élément d’ensemble à un élément d’ancrage, on utilise la propriété anchor-name
, le nom doit être préfixé par 2 tirets comme pour les variables CSS .
.anchor {
anchor-name: --myAnchor;
width: fit-content;
}
Ensuite, nous devons convertir notre élément à positionner par ancrage.
Tout d’abord en lui donnant une position absolue ou fixe, puis il faut lui appliquer la référence de l’ancre avec la propriété position-anchor
.
.anchor-positioned {
position: absolute;
position-anchor: --myAnchor;
}
Démo Démo
Vous pouvez tester sur le lien de ce Codepen .
<button class="small anchor" type="button" >
<svg
aria-label="hidden"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path
fill="currentColor"
d="M10.6 7.6c-1.6.8-3 2.1-4.4 4-.2.3-.2.4 0 .6.1.1.2.1.4-.2l.1-.1c.2-.2.3-.4.6-.7 1-1.2 1.5-1.7 2.3-2.1.7-.4 1.3.2 1.1.9 0 .2-.1.4-.2.7-.1.1-.1.2-.1.3-.3.7-.5 1.2-.9 2.4v.1c-.1.3-.1.3-.3.7-.6 1.5-.9 2.4-1.3 3.4v.1c-.1.3-.1.4-.2.5-.4 1.1-.7 1.9-.8 2.6 0 .1 0 .1-.1.2-.1.4-.2.7-.2 1-.1.5 0 .9.1 1.3.2.4.6.7 1 .7.7.1 1.4.1 2.1 0l1-.3c1-.4 1.9-.9 2.8-1.7.8-.7 1.5-1.5 2.3-2.4.1-.1.1-.1.1-.2.2-.3.4-.5.4-.7 0-.2-.1-.4-.3-.4-.1 0-.2.1-.3.3-.1.1-.1.2-.2.2l-.6.6c-.3.4-.7.7-1.2 1.3-.3.3-.6.5-.9.7-.6.3-1.1-.1-1-.7 0-.4.1-.8.3-1.3.3-.9.6-1.6 1.2-3.2.3-.7.4-1 .5-1.4.4-1.1.7-2 1.1-2.9.4-1.1.6-1.9.7-2.7.1-.6-.1-1.2-.5-1.6-.4-.5-1.1-.7-1.8-.7-.8 0-1.8.2-2.8.7zM16.5.2c-1.5-.6-3.4.4-3.7 2.1-.2 1.2.2 2.2 1.1 2.6 1.8.8 3.9-.5 3.9-2.5.1-1.1-.4-1.9-1.3-2.2z"
></path>
</svg>
</button>
<article class="box box-success anchor-positioned">
<span>Lorem ipsum dolor sit amet</span>
</article>
.anchor {
anchor-name: --myAnchor;
}
.anchor-positioned {
position: fixed;
position-anchor: --myAnchor;
padding: 1rem;
}
Anchor attribute Anchor attribute
Il existe également un attribut anchor
( voir la doc ) qui permet de définir un élément d’ancrage pour un élément d’ensemble.
Expérimental
Mais attention, l’attribut anchor est encore expérimental et n’est pas encore supporté par tous les navigateurs. A ce jour, seul Chrome Canary le supporte.
<div class="anchor" id="example-anchor">⚓︎</div>
<div class="infobox" anchor="example-anchor">
<p>This is an information box.</p>
</div>
Positionnement avec anchor() Positionnement avec anchor()
Introduction Introduction
Maintenant que nous avons lié nos éléments, nous avons besoin de gérer le positionnement par rapport à l’élément d’ancrage. Pour cela, il existe plusieurs manières de le faire :
- définir une valeur sur la fonction
anchor()
( voir la doc ) - spécifier une zone d’encart
- Utiliser les valeurs de placemnent du centre d’ancrage (
justify-*
)
Note
L’élément d’ancrage doit être visible dans le DOM (pas de display:none;
) sinon l’élément sera positionné par rapport à son ancêtre le plus proche.
Il est possible de gérer ce genre de cas avec du masquage conditionnel et la propriété position-visibility
Comme nous l’avons précisé plus haut, l’élément ancré doit être en position absolue ou fixe. Normalement, ces positions sont censées se référer à la fenêtre.
Mais le système des ancres changent ce comportement pour se référer à l’élément d’ancrage.
La fonction anchor()
va nous permettre de définir la position de l’élément ancré par rapport à l’élément d’ancrage.
anchor(<anchor-element> <anchor-side>, <length-percentage>)
Note
La fonction anchor()
renvoie une valeur de longueur, on peut donc utiliser des fonctions de calculs comme calc()
ou clamp()
.
Valeurs Valeurs
Parmi les valeurs courantes, on retrouve :
top
,bottom
,left
,right
pour les côtés de l’élémentcenter
pour le centre de l’élément
Ensuite, les valeurs logiques selon l’axe de la propriété inset
et le sens de lecture (writing-mode
):
start
,end
pour les côtés en fonction du sens de lectureself-start
,self-end
pour laisser le navigateur gérer la position
Exemple Exemple
Ancre implicite Ancre implicite
Les exemples précédents utilisent une ancre implicite (on ne précise pas de nom) car on part du principe qu’il n’y aura qu’une ancre associée. Comme dans cet exemple :
.anchored {
/* top de l'élément ancré et bottom de l'ancre */
top: anchor(bottom);
/* left de l'élément ancré et 50% de l'ancre */
left: anchor(50%);
}
Ancre explicite Ancre explicite
On peut aussi définir explicitement le nom de l’ancre soit par souci de maintenabilité soit pour gérer plusieurs ancres.
.anchored {
position: absolute;
top: anchor(--one bottom);
left: anchor(--one right);
right: anchor(--two left);
bottom: anchor(--two top);
}
Dans cet exemple, nous avons 2 ancres sur lesquelles nous positionnons notre élément. Les ancres étant en absolue et positionnées dans les coins de l’écran, si on redimentionne la fenêtre, l’élément ancré va s’étirer car il est tenu par les 2 ancres à la fois.
Fallback Fallback
La fonction anchor()
prévoit une valeur de fallback si l’élément d’ancrage n’est pas trouvé.
.anchored {
top: anchor(bottom, 50%);
}
Centrage Centrage
On peut centrer un élément ancré sur lui-même avec la propriété justify-self
et la valeur anchor-center
.
Positionnement avec inset-area Positionnement avec inset-area
En plus du positionnement directionnel par défaut issu du positionnement absolu, il existe un nouveau mécanisme de mise en page inclus
dans l’API d’ancrage, appelé zone d’encart.
La zone d’encart facilite le positionnement des éléments positionnés par rapport à leurs ancrages respectifs et fonctionne sur une grille à neuf cellules avec l’élément d’ancrage au centre.
La propriété à utiliser est inset-area
qui peut prendre les valeurs suivantes :
- top, right, left, bottom, center (valeurs physiques)
- block-start, inline-start, … (valeurs logiques)
- y-start, x-start, … (valeurs de coordonnées)
- span-start, span-left, span-all, … (permet de définir comment doit s’étendre le contenu)
Il existe une démo qui illustre parfaitement le concept de zone d’encart.
Dimensionner avec anchor-size() Dimensionner avec anchor-size()
L’API de positionnement d’ancre permet également de définir la taille d’un élément ancré en fonction de la taille de son ancre avec la fonction anchor-size()
.
Ajustement des positions Ajustement des positions
Une fois que l’on a défini la position de l’ancre, il est possible d’ajuster son comportement en fonction de sa proximité avec les bords du conteneur. En effet, il est primordial de s’assurer que les éléments ancrés s’affichent toujours de manière optimale pour l’utilisateur. Il sera même parfois préférable de masquer l’élément ancré si celui-ci déborde du conteneur et que son affichage n’a plus de sens.
Valeurs prédéfinies Valeurs prédéfinies
Il existe des modes de basculement déjà prédéfinis pour les navigateurs, dans ce cas, il n’est pas utile de créer des déclarations de @position-try
mais juste d’utiliser les bons mots-clés.
Ces valeurs inversent la position sur un ou deux axes si l’élement ancré déborde du conteneur.
flip-block
: inverse la position sur l’axe blockflip-inline
: inverse la position sur l’axe inlineflip-block flip-inline
: inverse la position sur les 2 axes
.anchor-positioned {
position-try-fallbacks: flip-block, flip-inline;
}
Note
On peut donc spécifier plusieurs valeurs de secours, elles doivent séparées par des virgules et elles seront appliquées dans l’ordre.
Avec les zones d’encart (inset-area) Avec les zones d’encart (inset-area)
Cette méthode est plus précise et permet de gérer les débordements de manière plus fine.
.anchor-positioned {
position: fixed;
position-anchor: --myAnchor;
inset-area: top left;
position-try-fallbacks:
top, top right, right,
bottom right, bottom,
bottom left, left;
}
Fallbacks personnalisés Fallbacks personnalisés
On peut aller encore plus loin en définissant des fallbacks personnalisés.
Un peu comme on le ferait avec des media queries, on va utiliser la règle @position-try
et la propriété position-try-fallbacks
.
Renommage
Cette fonctionnalité étant expérimentale, elle est sujette aux changements, par exemple position-try-options
a été renommée position-try-fallbacks
.
Note
Si vous souhaitez appliquer des transitions sur les basculements, vous devrez obligatoirement utiliser les positionnements avec anchor()
car à l’heure actuelle, il n’est pas possible de le faire sur la propriété inset-area
.
Position-try-order Position-try-order
Cette propriété sert principalement à indiquer au navigateur quel fallback utiliser en fonction de ce que l’on souhaite dès l’affichage de l’élément ancré.
Par exemple, si l’on souhaite que l’élément ancré soit toujours visible, on peut utiliser la valeur most-height
pour prioriser les positions qui maximisent la hauteur de l’élément ancré.
.anchor-positioned {
position-try-order: most-height;
}
Voici les valeurs possibles :
- normal
- most-height : priorise les positions qui maximisent la hauteur de l’élément ancré
- most-width : priorise les positions qui maximisent la largeur de l’élément ancré
- most-block-size: priorise les positions qui maximisent la taille de bloc de l’élément ancré
- most-inline-size: priorise les positions qui maximisent la taille en ligne de l’élément ancré
Position-visibility Position-visibility
La plupart du temps lorsque l’affichage de l’élément ancré pose problème, on cherchera à adapter la position en fonction de l’espace disponible
comme on l’a vu avec @position-try
.
Toutefois, dans certaines situations, on aimerait pouvoir masquer cet élément, par exemple dans un conteneur dans lequel on aurait un défilement
et qui contiendrait une tooltip d’information.
On peut le gérer avec le propriété position-visibility
( Voir la doc ) qui possède 3 valeurs :
- always (valeur par défaut)
- anchors-visible (masque dès que l’ancre n’est plus visible)
- no-overflow (masque dès que l’élément ancré déborde)
Support Support
Comme préciser au début de cet article, le module Anchor positioning n’est pas encore supporté par tous les navigateurs.
Vous pouvez détecter la compatibilité avec la règle @support
et proposer une alternative.
@supports (anchor-name: --myanchor) {
/* Anchor styles here */
}
Il existe un polyfill pour les anciens navigateurs.
Accessibilité Accessibilité
L’API ne définit pas nativement de relation sémantique entre l’élément d’ancrage et l’élément positionné. Toutefois, vous pouvez ajouter un attribut aria-details
pour améliorer l’accessibilité.
<button class="anchor" aria-details="more-info">Get more info</div>
<div class="positioned" id="more-info">More info content</div>
Il ne sera pas nécessaire de le faire si vous utilisez le positionnement de l’ancrage avec l’attribut popover
ou avec l’élément <dialog>
car l’accessibilité est bien gérée.
Liens Liens
- https://anchor-tool.com/
- https://blog.logrocket.com/use-css-anchor-positioning/
- https://developer.chrome.com/blog/anchor-positioning-api/
- https://www.w3.org/TR/css-anchor-position-1/
- https://frontendmasters.com/blog/drawing-a-line-to-connect-elements-with-css-anchor-positioning/
Conclusion Conclusion
En conclusion, le module Anchor positioning est une nouvelle fonctionnalité CSS qui permet de gérer le positionnement d’un élément par ancrage à un autre élément. Il offre une grande flexibilité pour le positionnement des éléments et permet de gérer des cas d’usage qui étaient jusqu’à présent réservés à JavaScript.