Back to Discover
MotionAdvanced
Physics Dock
An Apple OS-style floating dock where icons smoothly magnify based on mouse proximity.
Live Preview
"use client";
import { motion, useMotionValue, useTransform, useSpring } from "framer-motion";
import { useRef } from "react";
import { Home, Search, Bell, Settings, User } from "lucide-react";
export function PhysicsDock() {
const mouseX = useMotionValue(Infinity);
return (
<div className="flex items-center justify-center w-full h-40 bg-[#0a0a0a] rounded-xl border border-border">
<motion.div
onMouseMove={(e) => mouseX.set(e.pageX)}
onMouseLeave={() => mouseX.set(Infinity)}
className="flex items-end gap-4 rounded-2xl bg-[#111] px-4 pb-3 pt-4 border border-border shadow-xl"
>
{[
{ icon: Home, label: "Home" },
{ icon: Search, label: "Search" },
{ icon: Bell, label: "Notifications" },
{ icon: Settings, label: "Settings" },
{ icon: User, label: "Profile" },
].map((item, i) => (
<DockIcon key={i} mouseX={mouseX}>
<item.icon className="h-6 w-6 text-foreground" />
</DockIcon>
))}
</motion.div>
</div>
);
}
function DockIcon({ mouseX, children }: { mouseX: any; children: React.ReactNode }) {
const ref = useRef<HTMLDivElement>(null);
const distance = useTransform(mouseX, (val: number) => {
const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 };
return val - bounds.x - bounds.width / 2;
});
const widthSync = useTransform(distance, [-150, 0, 150], [40, 80, 40]);
const width = useSpring(widthSync, { mass: 0.1, stiffness: 150, damping: 12 });
return (
<motion.div
ref={ref}
style={{ width, height: width }}
className="flex items-center justify-center rounded-full bg-zinc-800 border border-zinc-700 shadow-md cursor-pointer hover:bg-zinc-700 transition-colors"
>
{children}
</motion.div>
);
}