Installation
terminal
npx shadcn@latest add https://ui.thilina.dev/r/animated-background.jsonUsage
components/nav.tsx
import { AnimatedBackground } from "@/components/animated-background";
export function Nav() {
return (
<AnimatedBackground className="bg-accent rounded-md" enableHover>
{["Overview", "Analytics", "Settings"].map((item) => (
<button key={item} data-id={item} className="px-3 py-1.5 text-sm">
{item}
</button>
))}
</AnimatedBackground>
);
}Every direct child needs a unique data-id. With enableHover the highlight
follows the pointer; without it, the highlight moves on click and behaves like
a segmented control. Children receive data-checked="true" while active, so
you can style the selected state with data-[checked=true]: utilities.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactElement[] | - | Items to track. Each needs a unique data-id attribute. |
className | string | - | Classes for the sliding highlight layer itself. |
defaultValue | string | null | - | The data-id that starts highlighted. |
onValueChange | (id: string | null) => void | - | Called when the active item changes. |
enableHover | boolean | false | Move the highlight on hover instead of click. |
transition | Transition | - | Motion transition for the glide between items. |
Accessibility
The highlight is a purely visual layer; your children stay real buttons or
links with their own semantics and focus behavior. Under
prefers-reduced-motion the highlight still tracks the active item, it just
jumps instantly instead of gliding.