Usage
import { HoverNav } from "@/components/motionkit/hover-nav";
<HoverNav
current={pathname}
links={[
{ label: "Work", href: "/work" },
{ label: "Services", href: "/services" },
{ label: "Studio", href: "/studio" },
{ label: "Contact", href: "/contact" },
]}
/>Motion
The exact curves this component runs, plotted with Motion's own maths.
Pill glide
Pointer or focus moves
Works in the App Router as is. Requires Tailwind CSS v4.
npm install motionSave as components/motionkit/hover-nav.tsx.
"use client";
import { useId, useState } from "react";
import { motion, useReducedMotion } from "motion/react";
export type NavLink = {
label: string;
href: string;
};
type HoverNavProps = {
links: NavLink[];
/** href of the current page; it gets aria-current and a dot. */
current?: string;
/** Accessible name for the nav landmark. */
label?: string;
className?: string;
};
export function HoverNav({ links, current, label = "Main", className = "" }: HoverNavProps) {
const [hovered, setHovered] = useState<string | null>(null);
const reduceMotion = useReducedMotion();
const pillId = useId();
return (
<nav aria-label={label} className={className}>
<ul
onPointerLeave={() => setHovered(null)}
className="flex items-center gap-1 rounded-full border border-white/10 bg-[#141B1B]/80 p-1 backdrop-blur"
>
{links.map((link) => {
const isCurrent = link.href === current;
return (
<li key={link.href} className="relative">
{hovered === link.href && (
<motion.span
layoutId={pillId}
aria-hidden
className="absolute inset-0 rounded-full bg-white/[0.08]"
transition={reduceMotion ? { duration: 0 } : { type: "spring", bounce: 0.2, duration: 0.35 }}
/>
)}
<a
href={link.href}
aria-current={isCurrent ? "page" : undefined}
// keyboard focus moves the pill too, not just the mouse
onPointerEnter={() => setHovered(link.href)}
onFocus={() => setHovered(link.href)}
onBlur={() => setHovered(null)}
className={`relative flex items-center gap-1.5 rounded-full px-4 py-2 text-sm transition-colors focus-visible:outline-2 focus-visible:outline-[#5FAEB4] ${
isCurrent ? "text-[#F6F1E8]" : "text-[#94A9A7] hover:text-[#F6F1E8]"
}`}
>
{isCurrent && <span aria-hidden className="size-1.5 rounded-full bg-[#5FAEB4]" />}
{link.label}
</a>
</li>
);
})}
</ul>
</nav>
);
}
| Prop | Type | Default | Description |
|---|---|---|---|
| links | { label, href }[] | — | The navigation links. |
| current | string | — | href of the current page. |
| label | string | "Main" | Accessible name for the nav landmark. |
Overview
A sliding pill follows your selection.
Animated Tabs
Accessible tabs with a sliding indicator and soft content transitions.
Magnetic Dock
A floating icon bar that magnifies and lifts around the pointer, with tooltips and a gliding active indicator.
Command Menu
A ⌘K palette with live filtering, grouped results and a highlight that glides between them.