Liquid / Wave Transition
A full page transition that begins as a small circle at the user's click coordinate, rapidly expanding outward like a liquid ripple to overtake the screen and reveal the new view.
Use
Entering highly immersive states (e.g., full-screen image viewer, games)
Avoid
Fast, utilitarian navigation (e.g., switching simple tabs)
Safer Alternative
No safer default listed.
Risk Level
Low risk
Live Demo
Light View
Click anywhere to trigger the wave.
What It Is
A full page transition that begins as a small circle at the user's click coordinate, rapidly expanding outward like a liquid ripple to overtake the screen and reveal the new view.
✓When to Use
- Entering highly immersive states (e.g., full-screen image viewer, games)
- Navigating to an opposite-themed page (Light to Dark mode switch)
✕When NOT to Use
- Fast, utilitarian navigation (e.g., switching simple tabs)
Configuration Tips
- 01Animate a CSS clip-path: circle(0% at x y) expanding to circle(150% at x y) upon click.
You've Seen It In
AI Implementation Prompts
Move from a fast scaffold to production details, then tune timing and edge states.
Create a full screen overlay transition using clip-path: circle() that animates from a pinpoint to covering the entire viewport dynamically.
Reference Implementation
The same implementation approach used by the live preview, kept compact enough to inspect and adapt.
"use client";
import { motion, AnimatePresence } from 'framer-motion';
import { useState, useEffect } from 'react';
export function LiquidWaveTransitionDemo({ isPlaying = false }: { isPlaying?: boolean }) {
const [view, setView] = useState<'A' | 'B'>('A');
useEffect(() => {
if (!isPlaying) {
setView('A');
return;
}
const loop = setInterval(() => {
setView(prev => prev === 'A' ? 'B' : 'A');
}, 3000);
return () => clearInterval(loop);
}, [isPlaying]);
return (
<div className="flex h-64 w-full items-center justify-center overflow-hidden rounded-2xl bg-white shadow-sm ring-1 ring-stone-200 relative cursor-pointer" onClick={() => setView(v => v === 'A' ? 'B' : 'A')}>
{"text-stone-400 italic">/* View A - Light Mode Minimalist */}
<div className="absolute inset-0 w-full h-full bg-stone-50 flex items-center justify-center flex-col z-0">
<div className="w-16 h-16 bg-stone-200 rounded-full mb-4 animate-pulse flex items-center justify-center">
<span className="text-stone-400 font-bold">A</span>
</div>
<h2 className="text-xl font-bold text-stone-800">Light View</h2>
<p className="text-sm text-stone-500 mt-2">Click anywhere to trigger the wave.</p>
</div>
{"text-stone-400 italic">/* View B - Dark Mode Premium (Clipped over View A) */}
<AnimatePresence>
{view === 'B' && (
<motion.div
key="viewB"
className="absolute inset-0 w-full h-full bg-indigo-600 flex items-center justify-center flex-col z-10"
"text-stone-400 italic">// The CSS circle path starts small and expands to cover 150% of the screen (guaranteeing no empty corners)
initial={{ clipPath: "circle(0% at 50% 50%)", opacity: 0.8 }}
animate={{ clipPath: "circle(150% at 50% 50%)", opacity: 1 }}
exit={{ clipPath: "circle(0% at 50% 50%)", transition: { duration: 0.6, ease: [0.65, 0, 0.35, 1] } }}
transition={{ duration: 0.8, ease: [0.33, 1, 0.68, 1] }} "text-stone-400 italic">// smooth ease out
>
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.4, delay: 0.2 }}
className="w-16 h-16 bg-white/20 rounded-full mb-4 backdrop-blur-md border border-white/20 shadow-xl flex items-center justify-center text-white"
>
<span className="font-bold">B</span>
</motion.div>
<motion.h2
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.4, delay: 0.3 }}
className="text-xl font-bold text-white tracking-tight"
>
Dark Mode Wave
</motion.h2>
<motion.p
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.4, delay: 0.35 }}
className="text-sm text-indigo-200 mt-2"
>
Seamlessly flooding the viewport.
</motion.p>
</motion.div>
)}
</AnimatePresence>
</div>
);
}Related Effects
Ripple Effect
A circular wave that expands from the point of contact when a user clicks or taps an element, providing immediate spatial feedback.
Shared Element Transition
An element (like an image or card) seamlessly animates from its position in a list view into its final position in a detail view, bridging the context between two states.