12 CSS Ripple Effects01 / 12
CSS Water Ripple Click Effect
An interactive water-surface pond you click to create realistic multi-ring ripples — four concentric CSS rings (box-shadow glow, fade-out scale) spawn at the exact cursor coordinate via JS, plus a radial-gradient caustic shimmer overlay and animated floating bubble particles. Outfit on a deep ocean radial-gradient with phosphor-blue palette.
Published
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="rpl-01">
<div class="rpl-01__header">
<h1>Water Ripple Click Effect</h1>
<p>Click anywhere on the surface to create realistic water ripples</p>
</div>
<div class="rpl-01__pond" id="rpl-01-pond">
<div class="rpl-01__label">click the surface</div>
</div>
<div class="rpl-01__info">
<div class="rpl-01__stat">
<b id="rpl-01-count">0</b>
<span>Drops</span>
</div>
<div class="rpl-01__stat">
<b id="rpl-01-rings">0</b>
<span>Active Rings</span>
</div>
</div>
</div><div class="rpl-01">
<div class="rpl-01__header">
<h1>Water Ripple Click Effect</h1>
<p>Click anywhere on the surface to create realistic water ripples</p>
</div>
<div class="rpl-01__pond" id="rpl-01-pond">
<div class="rpl-01__label">click the surface</div>
</div>
<div class="rpl-01__info">
<div class="rpl-01__stat">
<b id="rpl-01-count">0</b>
<span>Drops</span>
</div>
<div class="rpl-01__stat">
<b id="rpl-01-rings">0</b>
<span>Active Rings</span>
</div>
</div>
</div>@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap');
.rpl-01,
.rpl-01 *,
.rpl-01 *::before,
.rpl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.rpl-01 ::selection {
background: #00d4ff;
color: #001a2e;
}
.rpl-01 {
--deep: #001a2e;
--mid: #003a5c;
--water: #00a8cc;
--foam: #00d4ff;
--glint: #7ef7ff;
--text: #e0f7ff;
font-family: 'Outfit', sans-serif;
background: radial-gradient(ellipse at 50% 0%, #003a5c, #001015 70%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 40px;
padding: 40px 20px;
color: var(--text);
overflow: hidden;
position: relative;
}
/* Animated depth lines */
.rpl-01::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(0deg, transparent 0px, transparent 48px, rgba(0,168,204,0.06) 48px, rgba(0,168,204,0.06) 50px);
pointer-events: none;
}
.rpl-01__header {
text-align: center;
z-index: 2;
}
.rpl-01__header h1 {
font-size: clamp(1.8rem, 5vw, 3rem);
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(135deg, var(--glint), var(--foam), var(--water));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.rpl-01__header p {
margin-top: 8px;
font-size: 1rem;
color: rgba(224,247,255,0.55);
font-weight: 300;
}
/* The water surface */
.rpl-01__pond {
width: min(700px, 100%);
height: 400px;
position: relative;
border-radius: 20px;
overflow: hidden;
cursor: crosshair;
border: 1px solid rgba(0,212,255,0.2);
box-shadow: 0 0 60px rgba(0,168,204,0.25), inset 0 0 80px rgba(0,20,40,0.6);
background: radial-gradient(ellipse at 50% 0%, rgba(0,168,204,0.18), transparent 60%), linear-gradient(180deg, #001f38 0%, #000e1a 100%);
z-index: 2;
}
/* Caustic shimmer overlay */
.rpl-01__pond::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 200% 80% at 50% 100%, rgba(0,168,204,0.12), transparent), repeating-linear-gradient(60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px), repeating-linear-gradient(-60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px);
animation: rpl-01-caustic 8s linear infinite;
pointer-events: none;
z-index: 1;
}
@keyframes rpl-01-caustic {
to {
background-position: 0 40px, 30px 0, -30px 0;
}
}
.rpl-01__pond::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 60%, rgba(0,10,20,0.5) 100%);
pointer-events: none;
z-index: 3;
}
/* A ripple ring spawned by JS */
.rpl-01__ripple {
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 2;
animation: rpl-01-wave 2.4s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}
@keyframes rpl-01-wave {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(1);
opacity: 0;
}
}
/* Three concentric ring sizes */
.rpl-01__ripple--a {
width: 60px;
height: 60px;
border: 2px solid rgba(0,212,255,0.9);
box-shadow: 0 0 12px rgba(0,212,255,0.5), inset 0 0 12px rgba(0,212,255,0.1);
}
.rpl-01__ripple--b {
width: 130px;
height: 130px;
border: 1.5px solid rgba(0,168,204,0.65);
box-shadow: 0 0 20px rgba(0,168,204,0.3);
animation-delay: 0.12s;
}
.rpl-01__ripple--c {
width: 220px;
height: 220px;
border: 1px solid rgba(0,120,160,0.4);
animation-delay: 0.26s;
}
.rpl-01__ripple--d {
width: 340px;
height: 340px;
border: 0.5px solid rgba(0,90,120,0.25);
animation-delay: 0.44s;
}
/* Center drop dot */
.rpl-01__drop {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #7ef7ff, #00d4ff);
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 4;
animation: rpl-01-drop 0.4s cubic-bezier(0.2, 0.9, 0.4, 1) forwards;
box-shadow: 0 0 14px #00d4ff;
}
@keyframes rpl-01-drop {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
60% {
transform: translate(-50%,-50%) scale(1.4);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(0.6);
opacity: 0;
}
}
.rpl-01__label {
position: absolute;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: rgba(0,212,255,0.4);
letter-spacing: 0.2em;
text-transform: uppercase;
z-index: 4;
pointer-events: none;
animation: rpl-01-pulse 2s ease-in-out infinite;
}
@keyframes rpl-01-pulse {
50% {
opacity: 0.2;
}
}
/* Ambient floating particles */
.rpl-01__particle {
position: absolute;
border-radius: 50%;
background: rgba(0,212,255,0.6);
pointer-events: none;
z-index: 1;
animation: rpl-01-float linear infinite;
}
@keyframes rpl-01-float {
0% {
transform: translateY(0) scale(1);
opacity: 0.6;
}
50% {
opacity: 0.2;
}
100% {
transform: translateY(-380px) scale(0.3);
opacity: 0;
}
}
.rpl-01__info {
display: flex;
gap: 32px;
z-index: 2;
flex-wrap: wrap;
justify-content: center;
}
.rpl-01__stat {
text-align: center;
}
.rpl-01__stat b {
display: block;
font-size: 1.8rem;
font-weight: 700;
color: var(--foam);
}
.rpl-01__stat span {
font-size: 0.75rem;
color: rgba(224,247,255,0.4);
letter-spacing: 0.15em;
text-transform: uppercase;
}
@media (prefers-reduced-motion: reduce) {
.rpl-01__pond::before,
.rpl-01__label,
.rpl-01__particle {
animation: none !important;
}
.rpl-01__ripple,
.rpl-01__drop {
animation-duration: 0.01ms !important;
}
}@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap');
.rpl-01,
.rpl-01 *,
.rpl-01 *::before,
.rpl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.rpl-01 ::selection {
background: #00d4ff;
color: #001a2e;
}
.rpl-01 {
--deep: #001a2e;
--mid: #003a5c;
--water: #00a8cc;
--foam: #00d4ff;
--glint: #7ef7ff;
--text: #e0f7ff;
font-family: 'Outfit', sans-serif;
background: radial-gradient(ellipse at 50% 0%, #003a5c, #001015 70%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 40px;
padding: 40px 20px;
color: var(--text);
overflow: hidden;
position: relative;
}
/* Animated depth lines */
.rpl-01::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(0deg, transparent 0px, transparent 48px, rgba(0,168,204,0.06) 48px, rgba(0,168,204,0.06) 50px);
pointer-events: none;
}
.rpl-01__header {
text-align: center;
z-index: 2;
}
.rpl-01__header h1 {
font-size: clamp(1.8rem, 5vw, 3rem);
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(135deg, var(--glint), var(--foam), var(--water));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.rpl-01__header p {
margin-top: 8px;
font-size: 1rem;
color: rgba(224,247,255,0.55);
font-weight: 300;
}
/* The water surface */
.rpl-01__pond {
width: min(700px, 100%);
height: 400px;
position: relative;
border-radius: 20px;
overflow: hidden;
cursor: crosshair;
border: 1px solid rgba(0,212,255,0.2);
box-shadow: 0 0 60px rgba(0,168,204,0.25), inset 0 0 80px rgba(0,20,40,0.6);
background: radial-gradient(ellipse at 50% 0%, rgba(0,168,204,0.18), transparent 60%), linear-gradient(180deg, #001f38 0%, #000e1a 100%);
z-index: 2;
}
/* Caustic shimmer overlay */
.rpl-01__pond::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 200% 80% at 50% 100%, rgba(0,168,204,0.12), transparent), repeating-linear-gradient(60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px), repeating-linear-gradient(-60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px);
animation: rpl-01-caustic 8s linear infinite;
pointer-events: none;
z-index: 1;
}
@keyframes rpl-01-caustic {
to {
background-position: 0 40px, 30px 0, -30px 0;
}
}
.rpl-01__pond::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 60%, rgba(0,10,20,0.5) 100%);
pointer-events: none;
z-index: 3;
}
/* A ripple ring spawned by JS */
.rpl-01__ripple {
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 2;
animation: rpl-01-wave 2.4s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}
@keyframes rpl-01-wave {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(1);
opacity: 0;
}
}
/* Three concentric ring sizes */
.rpl-01__ripple--a {
width: 60px;
height: 60px;
border: 2px solid rgba(0,212,255,0.9);
box-shadow: 0 0 12px rgba(0,212,255,0.5), inset 0 0 12px rgba(0,212,255,0.1);
}
.rpl-01__ripple--b {
width: 130px;
height: 130px;
border: 1.5px solid rgba(0,168,204,0.65);
box-shadow: 0 0 20px rgba(0,168,204,0.3);
animation-delay: 0.12s;
}
.rpl-01__ripple--c {
width: 220px;
height: 220px;
border: 1px solid rgba(0,120,160,0.4);
animation-delay: 0.26s;
}
.rpl-01__ripple--d {
width: 340px;
height: 340px;
border: 0.5px solid rgba(0,90,120,0.25);
animation-delay: 0.44s;
}
/* Center drop dot */
.rpl-01__drop {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #7ef7ff, #00d4ff);
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 4;
animation: rpl-01-drop 0.4s cubic-bezier(0.2, 0.9, 0.4, 1) forwards;
box-shadow: 0 0 14px #00d4ff;
}
@keyframes rpl-01-drop {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
60% {
transform: translate(-50%,-50%) scale(1.4);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(0.6);
opacity: 0;
}
}
.rpl-01__label {
position: absolute;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: rgba(0,212,255,0.4);
letter-spacing: 0.2em;
text-transform: uppercase;
z-index: 4;
pointer-events: none;
animation: rpl-01-pulse 2s ease-in-out infinite;
}
@keyframes rpl-01-pulse {
50% {
opacity: 0.2;
}
}
/* Ambient floating particles */
.rpl-01__particle {
position: absolute;
border-radius: 50%;
background: rgba(0,212,255,0.6);
pointer-events: none;
z-index: 1;
animation: rpl-01-float linear infinite;
}
@keyframes rpl-01-float {
0% {
transform: translateY(0) scale(1);
opacity: 0.6;
}
50% {
opacity: 0.2;
}
100% {
transform: translateY(-380px) scale(0.3);
opacity: 0;
}
}
.rpl-01__info {
display: flex;
gap: 32px;
z-index: 2;
flex-wrap: wrap;
justify-content: center;
}
.rpl-01__stat {
text-align: center;
}
.rpl-01__stat b {
display: block;
font-size: 1.8rem;
font-weight: 700;
color: var(--foam);
}
.rpl-01__stat span {
font-size: 0.75rem;
color: rgba(224,247,255,0.4);
letter-spacing: 0.15em;
text-transform: uppercase;
}
@media (prefers-reduced-motion: reduce) {
.rpl-01__pond::before,
.rpl-01__label,
.rpl-01__particle {
animation: none !important;
}
.rpl-01__ripple,
.rpl-01__drop {
animation-duration: 0.01ms !important;
}
}(() => {
const pond = document.getElementById('rpl-01-pond');
const countEl = document.getElementById('rpl-01-count');
const ringsEl = document.getElementById('rpl-01-rings');
let dropCount = 0;
let activeRings = 0;
// Spawn ambient particles
for (let i = 0; i < 12; i++) {
const p = document.createElement('div');
p.className = 'rpl-01__particle';
const size = Math.random() * 4 + 1;
p.style.cssText = `
width:${size}px; height:${size}px;
left:${Math.random()*100}%;
bottom:${Math.random()*100}%;
opacity:${Math.random()*0.6+0.1};
animation-duration:${Math.random()*6+4}s;
animation-delay:${Math.random()*-6}s;
`;
pond.appendChild(p);
}
pond.addEventListener('click', (e) => {
const rect = pond.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
dropCount++;
countEl.textContent = dropCount;
// spawn drop dot
const drop = document.createElement('div');
drop.className = 'rpl-01__drop';
drop.style.left = x + 'px';
drop.style.top = y + 'px';
pond.appendChild(drop);
// spawn 4 concentric rings
['a','b','c','d'].forEach((t, i) => {
const r = document.createElement('div');
r.className = `rpl-01__ripple rpl-01__ripple--${t}`;
r.style.left = x + 'px';
r.style.top = y + 'px';
pond.appendChild(r);
activeRings++;
ringsEl.textContent = activeRings;
r.addEventListener('animationend', () => {
r.remove();
activeRings = Math.max(0, activeRings - 1);
ringsEl.textContent = activeRings;
}, { once: true });
});
drop.addEventListener('animationend', () => drop.remove(), { once: true });
});
})();(() => {
const pond = document.getElementById('rpl-01-pond');
const countEl = document.getElementById('rpl-01-count');
const ringsEl = document.getElementById('rpl-01-rings');
let dropCount = 0;
let activeRings = 0;
// Spawn ambient particles
for (let i = 0; i < 12; i++) {
const p = document.createElement('div');
p.className = 'rpl-01__particle';
const size = Math.random() * 4 + 1;
p.style.cssText = `
width:${size}px; height:${size}px;
left:${Math.random()*100}%;
bottom:${Math.random()*100}%;
opacity:${Math.random()*0.6+0.1};
animation-duration:${Math.random()*6+4}s;
animation-delay:${Math.random()*-6}s;
`;
pond.appendChild(p);
}
pond.addEventListener('click', (e) => {
const rect = pond.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
dropCount++;
countEl.textContent = dropCount;
// spawn drop dot
const drop = document.createElement('div');
drop.className = 'rpl-01__drop';
drop.style.left = x + 'px';
drop.style.top = y + 'px';
pond.appendChild(drop);
// spawn 4 concentric rings
['a','b','c','d'].forEach((t, i) => {
const r = document.createElement('div');
r.className = `rpl-01__ripple rpl-01__ripple--${t}`;
r.style.left = x + 'px';
r.style.top = y + 'px';
pond.appendChild(r);
activeRings++;
ringsEl.textContent = activeRings;
r.addEventListener('animationend', () => {
r.remove();
activeRings = Math.max(0, activeRings - 1);
ringsEl.textContent = activeRings;
}, { once: true });
});
drop.addEventListener('animationend', () => drop.remove(), { once: true });
});
})();Here's a working CSS Ripple Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Water Ripple Click Effect
Source: https://codefronts.com/motion/css-ripple-designs/css-water-ripple-click-effect/
An interactive water-surface pond you click to create realistic multi-ring ripples — four concentric CSS rings (box-shadow glow, fade-out scale) spawn at the exact cursor coordinate via JS, plus a radial-gradient caustic shimmer overlay and animated floating bubble particles. Outfit on a deep ocean radial-gradient with phosphor-blue palette.
## HTML
```html
<div class="rpl-01">
<div class="rpl-01__header">
<h1>Water Ripple Click Effect</h1>
<p>Click anywhere on the surface to create realistic water ripples</p>
</div>
<div class="rpl-01__pond" id="rpl-01-pond">
<div class="rpl-01__label">click the surface</div>
</div>
<div class="rpl-01__info">
<div class="rpl-01__stat">
<b id="rpl-01-count">0</b>
<span>Drops</span>
</div>
<div class="rpl-01__stat">
<b id="rpl-01-rings">0</b>
<span>Active Rings</span>
</div>
</div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap');
.rpl-01,
.rpl-01 *,
.rpl-01 *::before,
.rpl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.rpl-01 ::selection {
background: #00d4ff;
color: #001a2e;
}
.rpl-01 {
--deep: #001a2e;
--mid: #003a5c;
--water: #00a8cc;
--foam: #00d4ff;
--glint: #7ef7ff;
--text: #e0f7ff;
font-family: 'Outfit', sans-serif;
background: radial-gradient(ellipse at 50% 0%, #003a5c, #001015 70%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 40px;
padding: 40px 20px;
color: var(--text);
overflow: hidden;
position: relative;
}
/* Animated depth lines */
.rpl-01::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(0deg, transparent 0px, transparent 48px, rgba(0,168,204,0.06) 48px, rgba(0,168,204,0.06) 50px);
pointer-events: none;
}
.rpl-01__header {
text-align: center;
z-index: 2;
}
.rpl-01__header h1 {
font-size: clamp(1.8rem, 5vw, 3rem);
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(135deg, var(--glint), var(--foam), var(--water));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.rpl-01__header p {
margin-top: 8px;
font-size: 1rem;
color: rgba(224,247,255,0.55);
font-weight: 300;
}
/* The water surface */
.rpl-01__pond {
width: min(700px, 100%);
height: 400px;
position: relative;
border-radius: 20px;
overflow: hidden;
cursor: crosshair;
border: 1px solid rgba(0,212,255,0.2);
box-shadow: 0 0 60px rgba(0,168,204,0.25), inset 0 0 80px rgba(0,20,40,0.6);
background: radial-gradient(ellipse at 50% 0%, rgba(0,168,204,0.18), transparent 60%), linear-gradient(180deg, #001f38 0%, #000e1a 100%);
z-index: 2;
}
/* Caustic shimmer overlay */
.rpl-01__pond::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 200% 80% at 50% 100%, rgba(0,168,204,0.12), transparent), repeating-linear-gradient(60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px), repeating-linear-gradient(-60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px);
animation: rpl-01-caustic 8s linear infinite;
pointer-events: none;
z-index: 1;
}
@keyframes rpl-01-caustic {
to {
background-position: 0 40px, 30px 0, -30px 0;
}
}
.rpl-01__pond::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 60%, rgba(0,10,20,0.5) 100%);
pointer-events: none;
z-index: 3;
}
/* A ripple ring spawned by JS */
.rpl-01__ripple {
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 2;
animation: rpl-01-wave 2.4s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}
@keyframes rpl-01-wave {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(1);
opacity: 0;
}
}
/* Three concentric ring sizes */
.rpl-01__ripple--a {
width: 60px;
height: 60px;
border: 2px solid rgba(0,212,255,0.9);
box-shadow: 0 0 12px rgba(0,212,255,0.5), inset 0 0 12px rgba(0,212,255,0.1);
}
.rpl-01__ripple--b {
width: 130px;
height: 130px;
border: 1.5px solid rgba(0,168,204,0.65);
box-shadow: 0 0 20px rgba(0,168,204,0.3);
animation-delay: 0.12s;
}
.rpl-01__ripple--c {
width: 220px;
height: 220px;
border: 1px solid rgba(0,120,160,0.4);
animation-delay: 0.26s;
}
.rpl-01__ripple--d {
width: 340px;
height: 340px;
border: 0.5px solid rgba(0,90,120,0.25);
animation-delay: 0.44s;
}
/* Center drop dot */
.rpl-01__drop {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #7ef7ff, #00d4ff);
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 4;
animation: rpl-01-drop 0.4s cubic-bezier(0.2, 0.9, 0.4, 1) forwards;
box-shadow: 0 0 14px #00d4ff;
}
@keyframes rpl-01-drop {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
60% {
transform: translate(-50%,-50%) scale(1.4);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(0.6);
opacity: 0;
}
}
.rpl-01__label {
position: absolute;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: rgba(0,212,255,0.4);
letter-spacing: 0.2em;
text-transform: uppercase;
z-index: 4;
pointer-events: none;
animation: rpl-01-pulse 2s ease-in-out infinite;
}
@keyframes rpl-01-pulse {
50% {
opacity: 0.2;
}
}
/* Ambient floating particles */
.rpl-01__particle {
position: absolute;
border-radius: 50%;
background: rgba(0,212,255,0.6);
pointer-events: none;
z-index: 1;
animation: rpl-01-float linear infinite;
}
@keyframes rpl-01-float {
0% {
transform: translateY(0) scale(1);
opacity: 0.6;
}
50% {
opacity: 0.2;
}
100% {
transform: translateY(-380px) scale(0.3);
opacity: 0;
}
}
.rpl-01__info {
display: flex;
gap: 32px;
z-index: 2;
flex-wrap: wrap;
justify-content: center;
}
.rpl-01__stat {
text-align: center;
}
.rpl-01__stat b {
display: block;
font-size: 1.8rem;
font-weight: 700;
color: var(--foam);
}
.rpl-01__stat span {
font-size: 0.75rem;
color: rgba(224,247,255,0.4);
letter-spacing: 0.15em;
text-transform: uppercase;
}
@media (prefers-reduced-motion: reduce) {
.rpl-01__pond::before,
.rpl-01__label,
.rpl-01__particle {
animation: none !important;
}
.rpl-01__ripple,
.rpl-01__drop {
animation-duration: 0.01ms !important;
}
}
```
## JavaScript
```js
(() => {
const pond = document.getElementById('rpl-01-pond');
const countEl = document.getElementById('rpl-01-count');
const ringsEl = document.getElementById('rpl-01-rings');
let dropCount = 0;
let activeRings = 0;
// Spawn ambient particles
for (let i = 0; i < 12; i++) {
const p = document.createElement('div');
p.className = 'rpl-01__particle';
const size = Math.random() * 4 + 1;
p.style.cssText = `
width:${size}px; height:${size}px;
left:${Math.random()*100}%;
bottom:${Math.random()*100}%;
opacity:${Math.random()*0.6+0.1};
animation-duration:${Math.random()*6+4}s;
animation-delay:${Math.random()*-6}s;
`;
pond.appendChild(p);
}
pond.addEventListener('click', (e) => {
const rect = pond.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
dropCount++;
countEl.textContent = dropCount;
// spawn drop dot
const drop = document.createElement('div');
drop.className = 'rpl-01__drop';
drop.style.left = x + 'px';
drop.style.top = y + 'px';
pond.appendChild(drop);
// spawn 4 concentric rings
['a','b','c','d'].forEach((t, i) => {
const r = document.createElement('div');
r.className = `rpl-01__ripple rpl-01__ripple--${t}`;
r.style.left = x + 'px';
r.style.top = y + 'px';
pond.appendChild(r);
activeRings++;
ringsEl.textContent = activeRings;
r.addEventListener('animationend', () => {
r.remove();
activeRings = Math.max(0, activeRings - 1);
ringsEl.textContent = activeRings;
}, { once: true });
});
drop.addEventListener('animationend', () => drop.remove(), { once: true });
});
})();
```Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Ripple Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Water Ripple Click Effect
Source: https://codefronts.com/motion/css-ripple-designs/css-water-ripple-click-effect/
An interactive water-surface pond you click to create realistic multi-ring ripples — four concentric CSS rings (box-shadow glow, fade-out scale) spawn at the exact cursor coordinate via JS, plus a radial-gradient caustic shimmer overlay and animated floating bubble particles. Outfit on a deep ocean radial-gradient with phosphor-blue palette.
## HTML
```html
<div class="rpl-01">
<div class="rpl-01__header">
<h1>Water Ripple Click Effect</h1>
<p>Click anywhere on the surface to create realistic water ripples</p>
</div>
<div class="rpl-01__pond" id="rpl-01-pond">
<div class="rpl-01__label">click the surface</div>
</div>
<div class="rpl-01__info">
<div class="rpl-01__stat">
<b id="rpl-01-count">0</b>
<span>Drops</span>
</div>
<div class="rpl-01__stat">
<b id="rpl-01-rings">0</b>
<span>Active Rings</span>
</div>
</div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap');
.rpl-01,
.rpl-01 *,
.rpl-01 *::before,
.rpl-01 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.rpl-01 ::selection {
background: #00d4ff;
color: #001a2e;
}
.rpl-01 {
--deep: #001a2e;
--mid: #003a5c;
--water: #00a8cc;
--foam: #00d4ff;
--glint: #7ef7ff;
--text: #e0f7ff;
font-family: 'Outfit', sans-serif;
background: radial-gradient(ellipse at 50% 0%, #003a5c, #001015 70%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 40px;
padding: 40px 20px;
color: var(--text);
overflow: hidden;
position: relative;
}
/* Animated depth lines */
.rpl-01::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(0deg, transparent 0px, transparent 48px, rgba(0,168,204,0.06) 48px, rgba(0,168,204,0.06) 50px);
pointer-events: none;
}
.rpl-01__header {
text-align: center;
z-index: 2;
}
.rpl-01__header h1 {
font-size: clamp(1.8rem, 5vw, 3rem);
font-weight: 700;
letter-spacing: -0.02em;
background: linear-gradient(135deg, var(--glint), var(--foam), var(--water));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.rpl-01__header p {
margin-top: 8px;
font-size: 1rem;
color: rgba(224,247,255,0.55);
font-weight: 300;
}
/* The water surface */
.rpl-01__pond {
width: min(700px, 100%);
height: 400px;
position: relative;
border-radius: 20px;
overflow: hidden;
cursor: crosshair;
border: 1px solid rgba(0,212,255,0.2);
box-shadow: 0 0 60px rgba(0,168,204,0.25), inset 0 0 80px rgba(0,20,40,0.6);
background: radial-gradient(ellipse at 50% 0%, rgba(0,168,204,0.18), transparent 60%), linear-gradient(180deg, #001f38 0%, #000e1a 100%);
z-index: 2;
}
/* Caustic shimmer overlay */
.rpl-01__pond::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 200% 80% at 50% 100%, rgba(0,168,204,0.12), transparent), repeating-linear-gradient(60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px), repeating-linear-gradient(-60deg, transparent 0px, transparent 20px, rgba(0,212,255,0.03) 20px, rgba(0,212,255,0.03) 21px);
animation: rpl-01-caustic 8s linear infinite;
pointer-events: none;
z-index: 1;
}
@keyframes rpl-01-caustic {
to {
background-position: 0 40px, 30px 0, -30px 0;
}
}
.rpl-01__pond::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 60%, rgba(0,10,20,0.5) 100%);
pointer-events: none;
z-index: 3;
}
/* A ripple ring spawned by JS */
.rpl-01__ripple {
position: absolute;
border-radius: 50%;
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 2;
animation: rpl-01-wave 2.4s cubic-bezier(0.2, 0.6, 0.4, 1) forwards;
}
@keyframes rpl-01-wave {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(1);
opacity: 0;
}
}
/* Three concentric ring sizes */
.rpl-01__ripple--a {
width: 60px;
height: 60px;
border: 2px solid rgba(0,212,255,0.9);
box-shadow: 0 0 12px rgba(0,212,255,0.5), inset 0 0 12px rgba(0,212,255,0.1);
}
.rpl-01__ripple--b {
width: 130px;
height: 130px;
border: 1.5px solid rgba(0,168,204,0.65);
box-shadow: 0 0 20px rgba(0,168,204,0.3);
animation-delay: 0.12s;
}
.rpl-01__ripple--c {
width: 220px;
height: 220px;
border: 1px solid rgba(0,120,160,0.4);
animation-delay: 0.26s;
}
.rpl-01__ripple--d {
width: 340px;
height: 340px;
border: 0.5px solid rgba(0,90,120,0.25);
animation-delay: 0.44s;
}
/* Center drop dot */
.rpl-01__drop {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #7ef7ff, #00d4ff);
transform: translate(-50%, -50%) scale(0);
pointer-events: none;
z-index: 4;
animation: rpl-01-drop 0.4s cubic-bezier(0.2, 0.9, 0.4, 1) forwards;
box-shadow: 0 0 14px #00d4ff;
}
@keyframes rpl-01-drop {
0% {
transform: translate(-50%,-50%) scale(0);
opacity: 1;
}
60% {
transform: translate(-50%,-50%) scale(1.4);
opacity: 1;
}
100% {
transform: translate(-50%,-50%) scale(0.6);
opacity: 0;
}
}
.rpl-01__label {
position: absolute;
bottom: 18px;
left: 50%;
transform: translateX(-50%);
font-size: 0.75rem;
color: rgba(0,212,255,0.4);
letter-spacing: 0.2em;
text-transform: uppercase;
z-index: 4;
pointer-events: none;
animation: rpl-01-pulse 2s ease-in-out infinite;
}
@keyframes rpl-01-pulse {
50% {
opacity: 0.2;
}
}
/* Ambient floating particles */
.rpl-01__particle {
position: absolute;
border-radius: 50%;
background: rgba(0,212,255,0.6);
pointer-events: none;
z-index: 1;
animation: rpl-01-float linear infinite;
}
@keyframes rpl-01-float {
0% {
transform: translateY(0) scale(1);
opacity: 0.6;
}
50% {
opacity: 0.2;
}
100% {
transform: translateY(-380px) scale(0.3);
opacity: 0;
}
}
.rpl-01__info {
display: flex;
gap: 32px;
z-index: 2;
flex-wrap: wrap;
justify-content: center;
}
.rpl-01__stat {
text-align: center;
}
.rpl-01__stat b {
display: block;
font-size: 1.8rem;
font-weight: 700;
color: var(--foam);
}
.rpl-01__stat span {
font-size: 0.75rem;
color: rgba(224,247,255,0.4);
letter-spacing: 0.15em;
text-transform: uppercase;
}
@media (prefers-reduced-motion: reduce) {
.rpl-01__pond::before,
.rpl-01__label,
.rpl-01__particle {
animation: none !important;
}
.rpl-01__ripple,
.rpl-01__drop {
animation-duration: 0.01ms !important;
}
}
```
## JavaScript
```js
(() => {
const pond = document.getElementById('rpl-01-pond');
const countEl = document.getElementById('rpl-01-count');
const ringsEl = document.getElementById('rpl-01-rings');
let dropCount = 0;
let activeRings = 0;
// Spawn ambient particles
for (let i = 0; i < 12; i++) {
const p = document.createElement('div');
p.className = 'rpl-01__particle';
const size = Math.random() * 4 + 1;
p.style.cssText = `
width:${size}px; height:${size}px;
left:${Math.random()*100}%;
bottom:${Math.random()*100}%;
opacity:${Math.random()*0.6+0.1};
animation-duration:${Math.random()*6+4}s;
animation-delay:${Math.random()*-6}s;
`;
pond.appendChild(p);
}
pond.addEventListener('click', (e) => {
const rect = pond.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
dropCount++;
countEl.textContent = dropCount;
// spawn drop dot
const drop = document.createElement('div');
drop.className = 'rpl-01__drop';
drop.style.left = x + 'px';
drop.style.top = y + 'px';
pond.appendChild(drop);
// spawn 4 concentric rings
['a','b','c','d'].forEach((t, i) => {
const r = document.createElement('div');
r.className = `rpl-01__ripple rpl-01__ripple--${t}`;
r.style.left = x + 'px';
r.style.top = y + 'px';
pond.appendChild(r);
activeRings++;
ringsEl.textContent = activeRings;
r.addEventListener('animationend', () => {
r.remove();
activeRings = Math.max(0, activeRings - 1);
ringsEl.textContent = activeRings;
}, { once: true });
});
drop.addEventListener('animationend', () => drop.remove(), { once: true });
});
})();
```