Dynamic Badge Positioning
Pastilles de compteur et de statut positionnées aux coins d'avatars circulaires — clic pour incrémenter avec animation spring rejouée par reflow forcé.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Navigation comprend 36 effets, dont 4 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Chaque .badge-avatar est en position: relative, ce qui crée un contexte de positionnement pour ses badges enfants. Le .badge-count se pose à top: -4px; right: -4px et le .badge-status à bottom: 0; right: 0 — coordonnées en pixels relatives aux coins de l'avatar. Si la taille de l'avatar change, les badges suivent automatiquement les coins sans aucun recalcul JS.
L'animation d'entrée du compteur, @keyframes badgePop, passe de scale(0) à scale(1) avec la courbe cubic-bezier(.34, 1.56, .64, 1). Le deuxième chiffre de contrôle (1.56) dépasse intentionnellement 1.0 : la courbe outrepasse la valeur cible avant de revenir en arrière, produisant un spring bounce sans aucune librairie d'animation.
Chaque clic incrémente le compteur puis rejoue l'animation en 3 étapes : annulation (badge.style.animation = 'none'), lecture de badge.offsetHeight pour forcer un reflow synchrone qui purge la valeur calculée du style, puis réassignation de la chaîne d'animation. Sans ce reflow, le navigateur fusionne les deux changements de style en une seule passe et ne rejoue pas une animation déjà terminée.
Le survol grossit l'avatar (scale(1.12)) et le badge (scale(1.2)) via des transitions CSS, toutes deux avec la même courbe cubic-bezier(.34, 1.56, .64, 1). Le badge grossit via le sélecteur .badge-avatar:hover .badge-count — un retour visuel secondaire sans une ligne de JS supplémentaire.
Accessibilité
- prefers-reduced-motion absent : le code ne détecte pas la préférence système de réduction de mouvement. L'animation
badgePopet les transitions hover jouent sans condition. Pour respecter WCAG 2.3.3, ajoutez@media (prefers-reduced-motion: reduce) { .badge-count { animation: none; } .badge-avatar { transition: none; } }. - Les
.badge-avatarsont des<div>cliquables sansrole="button"nitabindex="0"— ils ne sont ni focusables au clavier ni annoncés par les lecteurs d'écran. Pour un usage production, convertissez-les en<button>ou ajoutez ces deux attributs. - Contraste du compteur : texte blanc (
#fff) sur fond rouge (#ef4444) → ratio ≈ 4,5:1, niveau WCAG AA atteint pour du texte en gras de taille normale. - Les
.badge-label(En ligne, Occupé, Absent) fournissent un contexte textuel visible mais ne sont pas reliés à l'avatar viaaria-describedby— un lecteur d'écran ne les associera pas automatiquement à l'avatar et à son compteur.
Compatibilité navigateur
Repose sur position: absolute, CSS animation, CSS transition et le reflow via offsetHeight — combinaison universellement supportée depuis 2014.
Si les animations CSS sont désactivées (OS ou navigateur très ancien), les badges apparaissent à leur position finale sans spring — la disposition reste parfaitement lisible et fonctionnelle.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="badge-scene">
<div class="badge-avatar badge-avatar-1">
<!-- Emoji, image ou initiales -->
<span class="badge-count">3</span>
<span class="badge-status online"></span>
<span class="badge-label">En ligne</span>
</div>
<!-- Répéter .badge-avatar pour chaque utilisateur -->
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
| .badge-count { background } | #ef4444 | Couleur de fond du compteur de notifications. Remplacez par votre teinte brand : #3b82f6 (bleu), #10b981 (vert), #8b5cf6 (violet). |
| .badge-status.online/busy/offline { background } | #10b981 / #f59e0b / #6b7280 | Couleurs des trois états de présence. Adaptez à votre convention d'interface. |
| .badge-avatar { width / height } | 60px | Taille de l'avatar circulaire. Les badges suivent les coins à n'importe quelle taille — aucun recalcul JS. |
| .badge-count { animation: .4s … } | 0.4s | Durée de l'animation spring badgePop. Réduisez à 0,25s pour une UI dense et réactive ; augmentez à 0,6s pour un accent dramatique. |
| cubic-bezier(.34, 1.56, .64, 1) | 1.56 = overshoot | Deuxième chiffre > 1 = amplitude du rebond spring. 2.0 = très élastique ; 1.1 = subtil. Modifier dans animation et transition. |
| .badge-count { top / right } | -4px / -4px | Décalage en pixels par rapport à l'angle supérieur droit de l'avatar. Ajustez si le badge est tronqué par un overflow parent. |
| .badge-count { border } | 2px solid #0a0a0f | Bordure de séparation entre le badge et l'avatar. La couleur doit correspondre au fond de la scène pour l'effet de halo. |
| .badge-label { color } | #a5b4fc | Couleur du texte d'état sous l'avatar. Adaptez à votre thème (blanc, gris neutre, couleur accent). |
FAQ
.badge-count et .badge-status utilisent position: absolute avec des coordonnées en pixels relatives aux coins de .badge-avatar (son conteneur position: relative). Modifiez width et height de .badge-avatar : les badges suivent automatiquement, sans recalcul JS.badge.style.animation = 'none'), lit badge.offsetHeight pour forcer un reflow synchrone qui purge la valeur calculée, puis réassigne la chaîne d'animation. Sans ce reflow, le navigateur fusionne les changements de style en une seule passe et saute le re-déclenchement — c'est la méthode fiable sans setTimeout ni toggle de classe.querySelectorAll('.badge-avatar'). Pour des avatars insérés ultérieurement, réattachez l'écouteur manuellement sur chaque nouvel élément. Pour des insertions fréquentes, utilisez la délégation d'événement sur le parent : container.addEventListener('click', e => { const av = e.target.closest('.badge-avatar'); if (!av) return; /* logique badge */ }).