Dock
A macOS-style magnifying dock — icons scale smoothly based on their horizontal distance from the cursor, with labels floating up on hover.
Loading preview…
npx shadcn@latest add https://webberui.com/r/dock.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
64
40
140
<Dock />
Installation
npx shadcn@latest add https://webberui.com/r/dock.jsonInstalling also writes the --wb-duration-fast CSS variable into your global stylesheet.
Usage
import { Dock, DockItem } from "@/components/ui/dock";
import { Home, Search, Settings } from "lucide-react";
<Dock>
<DockItem label="Home" href="/">
<Home />
</DockItem>
<DockItem label="Search" onClick={() => console.log("search")}>
<Search />
</DockItem>
<DockItem label="Settings" href="/settings">
<Settings />
</DockItem>
</Dock>Props
Dock
| Prop | Type | Default | Description |
|---|---|---|---|
magnification | number | 64 | Icon size when the cursor is closest (px) |
baseSize | number | 40 | Icon size at rest (px) |
distance | number | 140 | Radius of the magnification effect (px): icons only scale up when the cursor is within this horizontal distance of their center |
className | string | — | Appended to the dock container's className |
children | React.ReactNode | — | Put DockItem children directly inside |
DockItem
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Icon label: shown above on hover or keyboard focus, and used as the aria-label |
onClick | React.MouseEventHandler | — | Click event (works for both the button and the link) |
href | string | — | When provided, renders as an <a> link; otherwise a <button> |
children | React.ReactNode | — | Icon content: any SVG or element; it automatically fills the icon area and scales proportionally with the magnification |
className | string | — | Appended to the individual icon button's className |
How it works
- Shared mouseX: the container's pointermove updates a shared cursor X coordinate (a
MotionValue), set toInfinitywhen the pointer leaves; everyDockItemusesuseTransformto map "horizontal distance from the cursor" onto a size range, then smooths it withuseSpring, producing the classic macOS magnification wave - The dock itself never jumps: the container height is fixed at
baseSizeplus padding with the icons bottom-aligned, so magnification overflows upward - Tooltip labels: hovering a single icon floats a small card above it via
AnimatePresence; the label is also thearia-label, and the card itself is hidden from assistive technology so nothing is read twice - Keyboard accessibility:
DockItemis a native<button>(an<a>whenhrefis present), so Tab focuses them one by one; onfocus-visibleit magnifies tomagnificationand shows the label just the same - Automatic touch fallback: it only responds to mouse pointer events (
pointerType === "mouse"), so on touch it is simply a row of equally sized ordinary buttons - When the user has "reduce motion" enabled at the system level, the magnification and label displacement animations are disabled; the DOM structure is completely unchanged and functionality is unaffected
- The component itself does not depend on any icon library — whatever SVG you put in
childrenautomatically fills the icon area
Equalizer Toggle
A sound toggle: the equalizer bars dance while it is on, and freeze in grayscale when it is off.
Dynamic Island Nav
A Dynamic Island-style pill navigation — the selected pill slides and morphs between items, the selected item's name expands from 0, and one more click morphs the island downward into a panel.