Chargement✨ Premium

Pulse Ring

Deux anneaux de 50 px s'élargissent de 25 à 75 px en 2 s ease-out, s'effacent et repartent — ::after décalé d'une demi-période. Pur CSS, aucun script.

CSSRadar

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. Chargement comprend 29 effets, dont 4 gratuits. Découvrir la catégorie →

3 exemples d'usage

Recherche d'un appareil à proximité — réglages Bluetooth — Pulse Ring exemple 1

① Recherche d'un appareil à proximité — réglages Bluetooth

QuandL'utilisateur ouvre les réglages Bluetooth (ou un écran de partage local) pour ajouter un appareil ; le double anneau pulse au centre de l'écran, comme un radar qui balaie, tant qu'aucun appareil ne répond.
PourquoiDeux anneaux qui naissent au centre, s'élargissent puis s'effacent, c'est littéralement l'image d'un signal radio qui quitte l'appareil et perd de la portée : la forme du loader est la métaphore du balayage, aucune icône n'a besoin de l'expliquer. Le déphasage d'une demi-période produit un ping par seconde — la cadence d'un radar — et ease-out fait jaillir chaque onde avant de la laisser mourir. Un spinner dirait « je calcule » et une barre promettrait une fin ; ici l'attente dépend d'un appareil tiers et n'a pas de terme connu. À 80 px de conteneur, l'expansion de 40 à 120 px se lit comme un balayage, pas comme un point qui clignote.
RéglagesConteneur porté à 80 × 80 px dans .loader-pulse (anneaux de 40 à 120 px), couleur #3b82f6 (bleu Bluetooth) sur les deux bordures ; trait 3 px, durée 2 s et délai ::after à 1 s conservés — un départ par seconde. Le pictogramme Bluetooth (30 px) est un élément frère centré en absolu sur le conteneur, pas un enfant de .loader-pulse.
Terminal de paiement sans contact — « Approchez votre carte » — Pulse Ring exemple 2

② Terminal de paiement sans contact — « Approchez votre carte »

QuandLe montant est affiché et le terminal attend qu'une carte, un téléphone ou une montre entre dans le champ NFC ; les anneaux partent du symbole sans contact tant que rien n'est détecté.
PourquoiLe symbole sans contact est déjà fait d'arcs concentriques : des anneaux qui naissent sous ce pictogramme et s'élargissent le prolongent en mouvement — ils dessinent le champ que la carte doit rejoindre et désignent son point d'entrée. Surtout, ce loader ne dit pas « le terminal travaille » (un spinner inviterait à patienter) mais « le terminal écoute, à vous d'agir » : un battement régulier, sans rotation ni fin annoncée, qui reste lisible sur le petit écran d'un terminal tenu à bout de bras grâce aux 120 px atteints par chaque onde.
RéglagesConteneur 80 × 80 px (anneaux de 40 à 120 px), bordures #fff et trait 3 px conservé pour rester nettes sur l'écran sombre du terminal ; durée 1,6 s, délai ::after à 0,8 s (demi-période) — une onde toutes les 0,8 s. Le pictogramme sans contact (34 px) est un frère absolu centré sur le conteneur : le premier anneau (40 px) apparaît juste à son bord.
Actualisation de données live — carte de tableau de bord — Pulse Ring exemple 3

③ Actualisation de données live — carte de tableau de bord

QuandUne carte du tableau de bord recharge ses données en arrière-plan ; l'indicateur remplace temporairement le contenu ou s'affiche en surimpression.
PourquoiLe pulse indéterminé correspond à une attente courte dont la durée est inconnue, fréquente dans les appels API périodiques. L'absence de JavaScript allège les tableaux de bord à nombreux composants.
RéglagesCouleur #34d399 (vert — convention « donnée fraîche »), conteneur 40 × 40 px, border-width: 2px, durée 1,8 s, délai ::after à 0,9 s.

Comment ça marche

L'effet repose sur deux pseudo-éléments (::before et ::after) du conteneur .loader-pulse (50 × 50 px, position: relative). Chacun est positionné en absolu sur inset: 0 — il occupe le même carré 50 × 50 px que le conteneur — avec border: 3px solid #6366f1 et border-radius: 50%, ce qui en fait un anneau. Aucun enfant HTML n'est nécessaire ; aucun JavaScript n'est chargé.

L'animation @keyframes pulse fait passer la transform de scale(.5) à scale(1.5) et l'opacity de 1 à 0, en 2 s ease-out, en boucle infinie. Le diamètre visible de l'anneau part de 25 px (50 × 0,5) et atteint 75 px (50 × 1,5) ; comme scale() s'applique aussi au trait, l'épaisseur apparente passe de 1,5 px au départ à 4,5 px à l'arrivée — l'anneau s'épaissit légèrement en s'élargissant. La courbe ease-out produit une expansion rapide dès le début qui ralentit vers la fin : l'anneau jaillit du centre puis flotte avant de s'effacer. ::after reçoit animation-delay: 1s, soit la demi-période exacte : à tout instant, un anneau naît tandis que l'autre s'efface — un nouveau départ par seconde, deux cycles alternés sans interruption.

Le code ne contient aucune référence à var(--…) (vérifié : zéro occurrence) ni aucun script. La propriété inset requiert Chrome 87+, Firefox 87+ ou Safari 14.1+. Le nom @keyframes pulse n'est pas préfixé : si la page de l'acheteur définit déjà une animation portant ce nom, l'anneau adoptera celle de la page — renommez l'animation si ce risque existe. Le HTML vendu contient la div.demo-preview du site ; l'acheteur n'a besoin que de <div class="loader-pulse"></div> et du CSS sans ce wrapper.

Accessibilité

  • prefers-reduced-motion absent du code — mesuré sous Playwright : animationPlayState: "running" avec prefers-reduced-motion: reduce émulé (Chromium). Les deux anneaux s'animent quelle que soit la préférence OS. Correctif : ajouter @media (prefers-reduced-motion: reduce) { .loader-pulse::before, .loader-pulse::after { animation: none; } }.
  • Aucun attribut d'accessibilité : ni role="status", ni aria-busy, ni aria-live, ni texte alternatif. Les lecteurs d'écran ne signalent pas le chargement. Pour un indicateur fonctionnel, ajouter role="status" sur .loader-pulse et un <span class="sr-only">Chargement…</span> à l'intérieur (masqué visuellement, visible des technologies d'assistance).
  • L'animation est exclusivement visuelle et ne transmet aucune information par la couleur seule : l'anneau est soit visible soit absent. Si l'état de chargement doit être communiqué, il faut un message textuel associé — l'effet seul ne suffit pas.
  • Le nom @keyframes pulse n'est pas préfixé : en cas de collision sur la page hôte, l'anneau peut adopter une animation étrangère et le comportement visuel n'est plus garanti. Renommer en loader-pulse-anim ou similaire si plusieurs animations « pulse » coexistent.

Compatibilité navigateur

CSS pur : nécessite @keyframes, transform: scale(), opacity, inset et border-radius: 50%. Aucune dépendance JS.

Chrome 87+✓ Complet
Firefox 87+✓ Complet
Safari 14.1+✓ Complet
Edge 87+✓ Complet
Mobile iOS 15+✓ Complet
Android Chrome 87+✓ Complet

Sans inset (Chrome < 87, Safari < 14.1) : les pseudo-éléments ne se positionnent plus dans le conteneur — remplacer par top: 0; right: 0; bottom: 0; left: 0 pour couvrir les navigateurs plus anciens. Sans animations CSS, les deux pseudo-éléments restent figés à scale(.5) — deux petits arcs statiques, ni erreur ni page cassée.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
<div class="loader-pulse"></div>
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

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éfautEffet
Taille du conteneur (CSS — width / height de .loader-pulse) 50 × 50 px Diamètre de départ (× 0,5) et d'arrivée (× 1,5) des anneaux. 24 px = indicateur bouton ; 80–100 px = centrage plein écran.
Couleur (CSS — border: 3px solid #6366f1) #6366f1 (indigo) Couleur des deux anneaux. Changer la valeur hex suffit ; rgba permet d'ajuster l'opacité initiale indépendamment de l'animation.
Épaisseur du trait (CSS — border: 3px solid …) 3 px Épaisseur nominale ; scale() la multiplie : visuellement 1,5 px au départ, 4,5 px à l'arrivée. Passer à 5 px pour des anneaux plus affirmés sur de grands formats.
Durée d'un cycle (CSS — animation: 2s …) 2 s Durée d'un aller complet scale(0,5) → scale(1,5). 1 s = pulsation rapide ; 3–4 s = respiration lente. Si vous changez cette valeur, ajustez le délai du second anneau à la moitié.
Décalage du second anneau (CSS — animation-delay: 1s sur ::after) 1 s (demi-période) Déphasage entre les deux anneaux. À la demi-période, les cycles alternent parfaitement — un départ par seconde. 0,5 s (quart de période) : les deux anneaux se chevauchent davantage, effet de « double battement ».
Échelle de départ (CSS — scale(.5) dans @keyframes pulse) 0,5 — anneau à 25 px de diamètre sur un conteneur de 50 px scale(0) : l'anneau naît d'un point (effet onde de choc). scale(0,8) : l'expansion est courte et subtile, déjà presque au centre.
Courbe de timing (CSS — ease-out) ease-out Expansion rapide au départ, ralentissement en fin. linear : expansion mécanique régulière. ease-in-out : départ et fin doux, mouvement plus symétrique — proche d'un sonar.

FAQ

fx-0518 est un visualiseur audio : il génère 5 anneaux en JavaScript via requestAnimationFrame, calcule un intervalle de battement simulé (500 + Math.sin(time × 0,001) × 100 ms, soit 1,7 à 2,5 pulsations par seconde), applique une interpolation linéaire sur un conteneur de 120 px et partage sa boucle rAF avec d'autres visualiseurs de la même IIFE. fx-0423 est un indicateur de chargement : 2 pseudo-éléments CSS, aucun JavaScript, durée fixe de 2 s ease-out, déphasage exact d'une demi-période. Les deux effets sont sans rapport fonctionnel.
À scale(0), l'anneau partirait d'un point géométrique — un effet « onde de choc » ou « goutte d'eau ». À scale(0,5), il commence déjà à 25 px de diamètre : le regard perçoit un anneau qui s'élargit et s'estompe, pas un cercle qui naît. Cela rend la pulsation plus douce et plus lisible à petite taille (24 px de conteneur). Pour l'effet « onde de choc », remplacer scale(.5) par scale(0) — le trait visuel sera alors 0 px au départ et 4,5 px à l'arrivée.
Déclarer une variable CSS sur le conteneur : .loader-pulse { --pr-dur: 2s; } et remplacer les valeurs dans animation: var(--pr-dur) ease-out … et dans animation-delay: calc(var(--pr-dur) / 2). Les deux anneaux restent automatiquement à la demi-période quelle que soit la durée, et plusieurs instances peuvent coexister avec des vitesses différentes en surchargeant --pr-dur sur chaque conteneur.