Leroy
Web Development

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:

  1. GSAP (GreenSock Animation Platform) - handles the smooth tweening of the blob's position and width with power2.out easing for that deceleration feel
  2. An SVG gooey filter - feGaussianBlur + feColorMatrix creates 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.

Related Posts