Adding a Gooey Menu Hover Effect with GSAP
Leroy - Jun 1, 2026 - 2 min read
The Idea
I wanted the navigation menu on this blog to feel alive. When you hover over a link, a blob should stretch and slide to it with a viscous, liquid-like motion - like honey or warm gum. Not just a color change or underline, but something that feels tactile and playful.
The Technique
The effect combines two things:
- GSAP (GreenSock Animation Platform) - handles the smooth tweening of the blob's position and width with
power2.outeasing for that deceleration feel - An SVG gooey filter -
feGaussianBlur+feColorMatrixcreates the liquid stretch effect where the blob appears to ooze between links
How It Works
When you hover over a nav link, a small JavaScript function measures the link's position and size using getBoundingClientRect(), then tells GSAP to animate the blob to those coordinates:
function moveBlob(target) {
var rect = target.getBoundingClientRect();
var navRect = nav.getBoundingClientRect();
gsap.to(blob, {
left: rect.left - navRect.left - 8,
width: rect.width + 16,
opacity: 1,
duration: 0.4,
ease: 'power2.out'
});
}
The SVG filter is the secret sauce. It lives as an inline SVG in the page body:
<svg style="position:absolute;width:0;height:0;" aria-hidden="true">
<defs>
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
<feColorMatrix in="blur" mode="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="goo" />
<feBlend in="SourceGraphic" in2="goo" />
</filter>
</defs>
</svg>
The feGaussianBlur softens the edges of the blob, and feColorMatrix sharpens them back with a threshold - creating the gooey merging effect where the blob appears to flow.
Why GSAP from CDN?
This project has no JavaScript bundler - it's a Go server that serves static files directly. Adding webpack or esbuild just for one animation felt like overkill. GSAP's CDN is fast, version-pinned, and only ~30KB gzipped. No build step needed.
Accessibility Considerations
prefers-reduced-motion: The animation completely disables itself if the user has this preference set- Keyboard navigation: The blob follows tab focus, not just mouse hover
- Touch devices: The animation is disabled on touch screens to avoid flickering before page navigation
- Screen readers: The blob element has
aria-hidden="true"and the SVG filter is visually hidden
Dark Mode Compatibility
The blob uses the theme's --accent CSS variable at 30% opacity via color-mix(). This means it automatically adapts to both light and dark themes without any extra code.
The Result
Hover over the navigation links at the top of this page. The blob slides with a satisfying deceleration, stretching between links that are close together. It's a small touch, but it makes the site feel more polished and interactive.