# Ripple Reveal - Skill Reference > Slideshow that reveals each incoming slide as a soft-edged circle growing from a configurable origin, driven by a CSS `mask` with `radial-gradient` and an animated `@property` custom property. ## What this teaches - CSS `mask: radial-gradient(...)` to clip a slide by an animatable radius - `@property --vm-rr-p` registered as `` so `@keyframes` can interpolate it inside `calc()` - Feather zone: two gradient stops offset from one radius by half of `--vm-rr-feather` - Params written as `--vm-rr-*` custom properties on the container, so changing a value needs no re-render - Monotonic `zIndex` counter + `setTimeout` cleanup for slide stacking with no `animationend` listeners - Ken Burns drift with a long CSS `transition` on the active slide's `img` - `mount(container, options)` returning a narrow handle (`setParams`, `dispose`) that restores the container ## Core concept A radial-gradient can't be animated directly, but its stop positions can, if they derive from a registered custom property. `--vm-rr-p` runs 0 to 1 in one `@keyframes` block and every length in the mask is computed from it. The soft edge is the gap between `--vm-rr-edge-in` and `--vm-rr-edge-out`, both offset from the radius by half of the feather, so the edge keeps a constant width while the circle grows, unlike a blur that widens. The max radius is `150%` so the circle clears every corner whatever the origin. ## Use it ### Embed (no build step) ```html
``` - Every key of `defaults` is a kebab-case `data-*` attribute: `originX` becomes `data-origin-x`. All are optional. - Numbers are read with `Number(...)`. `ease` and `background` are kept as strings. There are no boolean params. - Images come from the `` children (`currentSrc || src`). The embed has no attribute for an image list and the effect exports no `optionsFromElement`. - With fewer than 2 slides nothing animates. - The script exposes `window.RippleReveal` with `init(root)` (start any `[data-ripple-reveal]` added later, e.g. after injecting HTML) and `get(el)` (the running handle for an element), plus `mount`, `defaults` and the `RippleReveal` class. ```js RippleReveal.get(document.querySelector("[data-ripple-reveal]"))?.setParams({ feather: 40 }); ``` The embed does not check `prefers-reduced-motion`. To honour it, omit `data-ripple-reveal` and call `RippleReveal.mount(el)` only when motion is allowed (see Accessibility). ### Bundled apps Fetch the files into your project: ```bash npx vanilla-motion add ripple-reveal # writes ./vanilla-motion/ripple-reveal/ ``` ```ts import { mount } from "./vanilla-motion/ripple-reveal/ripple-reveal"; const container = document.querySelector("#hero")!; // must have a size // images: slide URLs. Omit it to use the elements already inside the container. const fx = mount(container, { images: ["/img/one.jpg", "/img/two.jpg", "/img/three.jpg"], originX: 0, originY: 0, duration: 500, }); fx.setParams({ feather: 40, ease: "linear" }); // partial update; changing `delay` restarts the timer fx.dispose(); // stops timers, removes the vars, restores the original children ``` Handle (`RippleRevealHandle`): ```ts interface RippleRevealHandle { setParams(next: Partial): void; dispose(): void; } ``` Options are `Partial & { images?: string[] }`. `images` is not a param: it cannot be set with `setParams`. The module also exports `defaults`, `RippleReveal` (with `static readonly version = "1.0.0"`), and the types `RippleRevealParams`, `RippleRevealOptions`, `RippleRevealHandle`. ### Params | Key | Default | Range / options | Meaning | |---|---|---|---| | `delay` | `5000` | 500 to 10000, step 100 (ms) | Time a slide stays before the next one ripples in. | | `duration` | `1000` | 100 to 4000, step 50 (ms) | Length of one reveal. | | `feather` | `20` | 0 to 100, step 1 (% of the element) | Width of the soft edge. `0` is a hard circle. | | `originX` | `50` | 0 to 100, step 1 (%) | Where the ripple starts across the width. `0` is the left edge, `100` the right. | | `originY` | `50` | 0 to 100, step 1 (%) | Where the ripple starts down the height. `0` is the top edge, `100` the bottom. | | `ease` | `"cubic-bezier(0.4, 0, 1, 1)"` | `"cubic-bezier(0.4, 0, 1, 1)"` (ease in), `"linear"`, `"cubic-bezier(0, 0, 0.2, 1)"` (ease out), `"cubic-bezier(0.65, 0, 0.35, 1)"` (ease in out) | CSS timing function of the expansion. Any valid CSS timing function also works in code. | | `zoom` | `1.1` | 1 to 1.5, step 0.01 | Scale the active slide's image drifts to over 10 s. `1` turns the drift off. | | `background` | `"#0a372a"` | `#RRGGBB` colour | Colour behind the slides. | Ranges are the editor's slider limits; the class does not clamp. ## Key code ### HTML structure ```html
``` ### CSS — the effect The class injects this once as `