Svelte 5 component · fractalsvelte-ui · Motion
RippleEffect
A tactile touch and click container that emits radial waves expanding from the exact pointer down coordinates. Calculates the furthest corner distance to ensure full-bleed ripple coverage before fading.
Install
RippleEffect is a Svelte 5 ripple effect component in @fractaldesign/fractalsvelte-ui. It works in SvelteKit and in any Svelte 5 app.
pnpm add @fractaldesign/fractalsvelte-uiImport
import { RippleEffect } from '@fractaldesign/fractalsvelte-ui';Usage
<RippleEffect color={'rgba(255, 255, 255, 0.35)'} duration={600} center={false}>Click me</RippleEffect>Props
Also accepts every attribute of HTMLAttributes, spread onto
the root element.
| Name | Type | Default |
|---|---|---|
color | string | 'rgba(255, 255, 255, 0.35)' |
duration | number | 600 |
center | boolean | false |
class | string | '' |
Snippets
| Name | Type | Default |
|---|---|---|
children | Snippet | — |
Source
Show the RippleEffect.svelte source
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLAttributes } from 'svelte/elements';
interface Props extends HTMLAttributes<HTMLDivElement> {
color?: string;
duration?: number;
center?: boolean;
class?: string;
children?: Snippet;
}
let {
color = 'rgba(255, 255, 255, 0.35)',
duration = 600,
center = false,
class: className = '',
children,
onpointerdown,
...restProps
}: Props = $props();
interface RippleItem {
id: number;
x: number;
y: number;
size: number;
}
let ripples = $state<RippleItem[]>([]);
let rippleCounter = 0;
function handlePointerDown(e: PointerEvent & { currentTarget: EventTarget & HTMLDivElement }) {
const rect = e.currentTarget.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
let x: number;
let y: number;
if (center) {
x = width / 2;
y = height / 2;
} else {
x = e.clientX - rect.left;
y = e.clientY - rect.top;
}
const radius = Math.max(
Math.hypot(x, y),
Math.hypot(width - x, y),
Math.hypot(x, height - y),
Math.hypot(width - x, height - y)
);
const size = radius * 2;
const id = ++rippleCounter;
ripples = [...ripples, { id, x, y, size }];
setTimeout(() => {
ripples = ripples.filter((r) => r.id !== id);
}, duration);
onpointerdown?.(e);
}
</script>
<div
class="f-ripple-wrapper {className}"
style="--ripple-color: {color}; --ripple-duration: {duration}ms;"
onpointerdown={handlePointerDown}
{...restProps}
>
{#if children}
{@render children()}
{/if}
<div class="f-ripple-plane" aria-hidden="true">
{#each ripples as r (r.id)}
<span
class="f-ripple-wave"
style="left: {r.x}px; top: {r.y}px; width: {r.size}px; height: {r.size}px;"
></span>
{/each}
</div>
</div>
<style>
@layer fractalsvelte-ui.components {
.f-ripple-wrapper {
position: relative;
overflow: hidden;
display: inline-block;
cursor: pointer;
user-select: none;
}
.f-ripple-plane {
position: absolute;
inset: 0;
pointer-events: none;
overflow: hidden;
border-radius: inherit;
z-index: 10;
}
.f-ripple-wave {
position: absolute;
border-radius: var(--f-radius-full);
background: radial-gradient(circle, var(--ripple-color) 0%, transparent 70%);
transform: translate(-50%, -50%) scale(0);
animation: f-ripple-anim var(--ripple-duration) cubic-bezier(0.1, 0.8, 0.3, 1) forwards;
pointer-events: none;
}
@keyframes f-ripple-anim {
0% {
transform: translate(-50%, -50%) scale(0);
opacity: 1;
}
100% {
transform: translate(-50%, -50%) scale(1);
opacity: 0;
}
}
}
</style>Related components
RouteTransition A spatial page and view transition boundary container providing native mobile-like navigation presets…SharedElement A declarative hero transition wrapper that smoothly morphs spatial geometry and border radius between views…ShimmerEffect A continuous diagonal light sweep overlay designed for attention highlights and loading skeleton containers.SplitReveal A dual-shutter curtain container that parts smoothly along horizontal or vertical axes to uncover underlying…SpotlightCard A presentation card illuminated by a cursor-tracking radial flashlight glow and dual-layer glowing perimeter…StaggerList A sequenced list container that animates children into view with staggered delay increments, slide…