Back to library
INTERACTION_120

Active Link Indicator.

A rule wipes in under the hovered link and stays on the active route. Scaled to 0 on the X axis from the left before the trigger; scaling the rule to full width, out to the right on leave once hover, focus or active route.

CATEGORY
Navigation
COMPLEXITY
Easy
TECH
CSS
PLATFORM
Both
PERFORMANCE
Light
LIVE PLAYGROUND
HomeWorkPricing
CSS / CSS
/* Active Link Indicator — A rule wipes in under the hovered link and stays on the active route. */
.active-link-indicator {
  --dur: 300ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --stagger: 60ms;
  will-change: transform, opacity;
}

.active-link-indicator > * {
  opacity: 0;
  transform: translateY(14px);
  animation: active-link-indicator-in var(--dur) var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}

/* scaled to 0 on the X axis from the left -> scaling the rule to full width, out to the right on leave */
@keyframes active-link-indicator-in {
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .active-link-indicator > * {
    opacity: 1;
    transform: none;
    animation: none;
  }
}
React / TSX
import { useEffect, useRef } from "react";

/** Active Link Indicator — trigger: hover, focus or active route */
export function ActiveLinkIndicator({ children }: { children: React.ReactNode }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
      el.dataset["state"] = "in";
      return;
    }
    const io = new IntersectionObserver(
      ([entry]) => {
        if (entry?.isIntersecting) {
          el.dataset["state"] = "in";
          io.unobserve(el);
        }
      },
      { threshold: 0.2 },
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return (
    <div ref={ref} className="active-link-indicator" data-state="out">
      {children}
    </div>
  );
}

IF YOU LIKE THIS, STEAL THESE TOO.

DISCOVEREFFECTSPROMPTS
INTERACTION_027CSS

Navigation Underline

Underline grows from the origin side on hover.

Navigation / LightView
Home
Work
Pricing
INTERACTION_117CSS

Animated Mobile Menu

The panel slides in and links cascade behind it.

Navigation / LightView
HomeWorkPricing
INTERACTION_119CSS

Expanding Navigation Pill

A pill indicator glides between nav items.

Navigation / LightView
HomeWorkPricing
INTERACTION_121CSS

Scroll Aware Navbar

The header hides on scroll down and returns on scroll up.

Navigation / LightView