Docs
Dock
Dock
An interactive dock component inspired by macOS dock with animation capabilities.
References
Inspiration
Framer Motion Courses (not affiliated)
Animation Teachers (also not affiliated)
Installation
npx shadcn@latest add https://cult-ui.com/r/dock.json
Usage
import {
Card,
Dock,
DockCard,
DockDivider,
useDock,
} from "@/components/Dock/Dock"
// Example usage of the Dock component with animated cards and dividers
const DockDemo = () => {
const gradients = [
"https://products.ls.graphics/mesh-gradients/images/03.-Snowy-Mint_1-p-130x130q80.jpeg",
"https://products.ls.graphics/mesh-gradients/images/04.-Hopbush_1-p-130x130q80.jpeg",
"https://products.ls.graphics/mesh-gradients/images/06.-Wisteria-p-130x130q80.jpeg",
"https://products.ls.graphics/mesh-gradients/images/09.-Light-Sky-Blue-p-130x130q80.jpeg",
"https://products.ls.graphics/mesh-gradients/images/12.-Tumbleweed-p-130x130q80.jpeg",
"https://products.ls.graphics/mesh-gradients/images/15.-Perfume_1-p-130x130q80.jpeg",
null,
"https://products.ls.graphics/mesh-gradients/images/36.-Pale-Chestnut-p-130x130q80.jpeg",
]
const openIcons = [
<CircleIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
<TriangleIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
<SquareIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
<PentagonIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
<HexagonIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
<OctagonIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
<OctagonIcon className="h-8 w-8 fill-black stroke-black rounded-full" />, // skip
<BlocksIcon className="h-8 w-8 fill-black stroke-black rounded-full" />,
]
return (
<div className="min-h-screen flex items-center justify-center">
<Dock>
{gradients.map((src, index) =>
src ? (
<DockCard key={src} id={`${index}`}>
<Card src={src} id={`${index}`}>
{openIcons[index]}
</Card>
</DockCard>
) : (
<DockDivider key={index} />
)
)}
</Dock>
</div>
)
}
export default DockDemo
Dock Component
// Main Dock component: orchestrating the dock's animation behavior
const Dock = ({ children }: DockProps) => {
// State to track if the dock is hovered. When the mouse hovers over the dock, this state changes to true.
const [hovered, setHovered] = useState(false)
// State to track the width of the dock. This dynamically updates based on the dock's current width.
const [width, setWidth] = useState(0)
// Reference to the dock element in the DOM. This allows direct manipulation and measurement of the dock.
const dockRef = useRef<HTMLDivElement>(null)
// Reference to track if the zooming animation is active. This prevents conflicting animations.
const isZooming = useRef(false)
// State to track which dock items are currently animating. This array holds the indices of animating items.
const [animatingIndexes, setAnimatingIndexes] = useState<number[]>([])
// Callback to toggle the zooming state. This ensures that we don't trigger hover animations while zooming.
const setIsZooming = useCallback((value: boolean) => {
isZooming.current = value // Update the zooming reference
setHovered(!value) // Update the hover state based on zooming
}, [])
// Motion value for the zoom level of the dock. This provides a smooth zooming animation.
const zoomLevel = useMotionValue(1)
// Hook to handle window resize events and update the dock's width accordingly.
useWindowResize(() => {
setWidth(dockRef.current?.clientWidth || 0) // Set width to the dock's current width or 0 if undefined
})
// Motion value to track the mouse's X position relative to the viewport. Initialized to Infinity to denote no tracking initially.
const mouseX = useMotionValue(Infinity)
return (
// Provide the dock's state and control methods to the rest of the application through context.
<DockContext.Provider
value={{
hovered, // Current hover state of the dock
setIsZooming, // Method to set the zooming state
width, // Current width of the dock
zoomLevel, // Current zoom level motion value
mouseX, // Current mouse X position motion value
animatingIndexes, // Current animating indexes
setAnimatingIndexes, // Method to set animating indexes
}}
>
<motion.div
ref={dockRef} // Reference to the dock element
className="fixed bottom-3 left-1/2 transform -translate-x-1/2 flex items-end h-14 p-2 gap-3 bg-black bg-opacity-90 rounded-xl"
// Event handler for mouse movement within the dock
onMouseMove={(e) => {
mouseX.set(e.pageX) // Update the mouseX motion value to the current mouse position
if (!isZooming.current) {
// Only set hovered if not zooming
setHovered(true) // Set hovered state to true
}
}}
// Event handler for when the mouse leaves the dock
onMouseLeave={() => {
mouseX.set(Infinity) // Reset mouseX motion value
setHovered(false) // Set hovered state to false
}}
style={{
x: "-50%", // Center the dock horizontally
scale: zoomLevel, // Bind the zoom level to the scale style property
}}
>
{children} {/* Render the dock's children within the motion div */}
</motion.div>
</DockContext.Provider>
)
}