Ripple Click
A Material Design button: on click, a circular wave ripples out from the exact point of contact, expands, and fades out. Three visual variants (violet, outline, dark), minimal JavaScript.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →
3 usage examples



How it works
The .fx-0364-root root (250 px tall, rgb(26, 26, 36) background) centers three .mi-ripple-btn buttons in .mi-ripple-scene. Each button carries the data-ripple attribute, which signals to the script that it should receive the effect. The buttons have position: relative; overflow: hidden — that overflow: hidden is what keeps the wave inside the button area.
On click, the script computes the pointer position within the button via getBoundingClientRect() and creates an absolutely positioned span.mi-ripple. Its size is twice the button’s largest dimension (to cover the corners), its center aligns with the click point, and it animates via @keyframes miRippleOut: scale from 0 to 1 over 0.6 s with ease-out and opacity from 0.3 to 0 over the same duration. At the end of the animation, the span is removed from the DOM to prevent accumulation.
Three CSS variants change only the colors: .mi-ripple-btn--purple (violet background #6366f1, white wave), .mi-ripple-btn--outline (transparent background, violet border, violet wave), .mi-ripple-btn--dark (dark background #1e1b4b, white wave). The HTML structure and animation are identical for all three. The script detects prefers-reduced-motion: if enabled, no span is created and the wave does not play.
Accessibility
- Keyboard: each button is a real
<button>; Tab reaches it, Space or Enter fires it. The focus outline (:focus-visible) appears on keyboard navigation only. - Reduced motion: under
prefers-reduced-motion: reduce, the JavaScript creates no wave span and the CSS removes the wave animation. The buttons remain fully functional visually (colors, hover, focus) with no animated elements. - Touch: the wave originates from the touch point on a touchscreen (via the
clickevent which reports finger coordinates). The minimum 44×44 px click target is met for each variant.
Browser compatibility
CSS (overflow: hidden, @keyframes animations) + 0.6 KB of JavaScript (getBoundingClientRect, createElement, addEventListener, matchMedia), no dependency. Verified in Chromium 151: wave at click point, span removal at animation end, three variants, reduced-motion with no wave, 0 errors. The preview captures show the animation auto-replayed by the scene code: this behaviour is not included in the sold code.
Without JavaScript, buttons are clickable and visually correct (hover, focus) but the wave does not appear. Without CSS animations, the span created by the JS stays visible until removal with no animation.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0364-root">
<div class="mi-ripple-scene">
<button class="mi-ripple-btn mi-ripple-btn--purple" data-ripple="">Label</button>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Color variant (HTML — modifier class on .mi-ripple-btn) |
3 variants: --purple, --outline, --dark |
Create a new variant in CSS by overriding background, color, border, and the .mi-ripple color. No JavaScript changes needed. |
Wave duration and easing (CSS — duration and easing of @keyframes miRippleOut) |
0.6 s, ease-out | Reduce to 0.3 s for a crisp, discreet wave; raise to 1 s for a slow, contemplative one. ease-in gives a slow start and fast end. |
Maximum wave opacity (CSS — starting opacity value in @keyframes miRippleOut) |
0.3 | 0.15 is very subtle; 0.5 is more visible. On the outline variant, the violet wave at 0.3 is already clearly legible on a light background. |
Activation on any element (HTML — data-ripple attribute) |
On the three demo buttons | Add data-ripple to any focusable element (<a>, <button>) in the same root. Make sure the element has position: relative; overflow: hidden. |
FAQ
overflow: hidden on the button contains the wave within its bounds. This is essential to keep the effect clean; do not remove this property.click event fired by a touch reports the contact point coordinates. The wave originates at the finger, not the button center. Tested in Chromium (touch emulation); identical behavior expected on real devices.