Ripple Effect
Onde circulaire Material Design créée au point de clic — zéro rAF, positionnement relatif, animation CSS pure scale+opacité, auto-nettoyage sur animationend.
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. Atmosphère comprend 57 effets, dont 10 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Au clic, getBoundingClientRect() convertit les coordonnées écran (clientX/Y) en coordonnées locales au conteneur. La soustraction clientX - rect.left donne la position exacte même si le conteneur est transformé CSS ou que la page est scrollée.
Un <div class="ripple"> est créé et inséré dans le conteneur à ces coordonnées. Sa taille est fixée à 100 × 100 px directement en JS ; il est centré sur le point de clic via marginLeft: -50px; marginTop: -50px. Le conteneur porte overflow: hidden — l'onde est clippée à ses bords, donnant l'illusion qu'elle émerge de l'intérieur du composant.
Toute l'animation est portée par une keyframe CSS (scale(0) → scale(N) + opacity 0.4 → 0) : aucun requestAnimationFrame ne tourne, aucune boucle JS n'est lancée. Le thread principal n'est pas bloqué. Plusieurs clics rapprochés créent plusieurs ondes .ripple indépendantes dans le DOM simultanément.
L'écouteur animationend supprime l'élément du DOM dès que la keyframe se termine — aucun timer, aucun nettoyage manuel. À noter : la classe .ripple et la keyframe CSS ne sont pas définies dans le CSS fourni (elles provenaient d'une page hôte) ; elles doivent être ajoutées à votre feuille de style. Les éléments .ripple nécessitent aussi pointer-events: none pour ne pas capturer les clics suivants.
Accessibilité
- prefers-reduced-motion absent : le code ne vérifie pas la préférence système — l'animation tourne toujours. Ajoutez
@media (prefers-reduced-motion: reduce) { .ripple { animation-duration: 0.01ms !important; } }pour queanimationendse déclenche quasi-instantanément sans bloquer le nettoyage DOM. - Le
.ripple-containeracursor: pointermais pas detabindex="0"ni derole="button"— non activable au clavier dans l'état fourni. Ajoutez les deux attributs et un listenerkeydown(Enter / Espace) si l'élément est interactif. - Aucun
aria-labelsur le conteneur dans le code d'origine. Si le texte intérieur ne décrit pas l'action clairement, ajoutez un libellé ARIA explicite. - Les éléments
.rippleinjectés ne portent paspointer-events: nonedans le CSS fourni — à ajouter pour éviter qu'une onde en cours capture les clics suivants et les empêche de déclencher leur propre onde. - Le fond
#12121aavec texte blanc dépasse WCAG AA (ratio > 10:1) — pas de problème de contraste sur la configuration par défaut.
Compatibilité navigateur
Utilise getBoundingClientRect(), addEventListener('animationend') et manipulation DOM — universels depuis Chrome 43 / Firefox 43 / Safari 9.
Si les animations CSS ne sont pas supportées, <code>animationend</code> ne se déclenche jamais et les <code>.ripple</code> restent dans le DOM. Ajoutez un <code>setTimeout</code> de secours à 1 s pour les retirer.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="ripple-container" id="rippleContainer">
<!-- Contenu du composant (texte, icône, libellé) -->
Cliquez ici
<!-- Les <div class="ripple"> sont injectés par le JS au clic et supprimés sur animationend -->
</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 |
|---|---|---|
| background (CSS .ripple) | rgba(255,255,255,0.4) | Couleur et opacité de l'onde. Utilisez la couleur brand avec une opacité 0.2–0.5 selon le contraste du fond. |
| animation-duration (CSS .ripple) | ~0.6s | Durée de l'expansion. 0.4s = snappy (bouton CTA), 0.8–1s = organique (hero, surfaces larges). |
| to { transform: scale(N) } (CSS @keyframes) | 4 | Facteur d'expansion final. Calibrez selon la taille du conteneur : N ≈ (2 × diagonale du conteneur) ÷ 100 pour que l'onde couvre l'intégralité de la surface. |
| ripple.style.width / height (JS lignes 7–8) | 100px | Taille initiale du disque. Augmenter adoucit le départ de l'onde ; réduire la rend plus ponctuelle. Ajustez toujours marginLeft = marginTop = -(taille / 2). |
| animation-timing-function (CSS .ripple) | ease-out | Easing de l'expansion. ease-out = départ vif, fin douce (Material Design). cubic-bezier(0.4,0,0.2,1) = courbe Material standard. |
| border-radius (CSS .ripple) | 50% | Forme de l'onde. 50% = cercle parfait. Réduire donne un losange arrondi ; 0 = onde carrée pour un rendu plus géométrique. |
FAQ
.ripple indépendant dans le conteneur. Plusieurs clics rapprochés génèrent des ondes qui se chevauchent — chacune se supprime individuellement sur son propre animationend.@media (prefers-reduced-motion: reduce) { .ripple { animation-duration: 0.01ms !important; } }. L'onde se termine quasi-instantanément, animationend se déclenche normalement et nettoie le DOM. Aucune modification JS nécessaire.position: relative; overflow: hidden sur le <button> — le comportement natif (focus, clavier, submit) est préservé puisque l'effet se contente d'insérer un élément enfant sans toucher aux événements.