VibeUI
ComponentsDocs
VibeUI

Components with taste, that you own. Copy the source, retint the tokens, keep every line.

Library

  • All components
  • Signature pieces
  • Text effects
  • Footers

Docs

  • Introduction
  • Installation
  • Theming
  • CLI

Project

  • GitHub
  • Agents and MCP
  • Issues
  • MIT license
© 2026 VibeUI · MIT licensed
  1. Components
  2. Signature
  3. Dock

Dock

macOS-style dock: icons magnify smoothly with distance from the cursor.

SignatureClient component

Install

bash
npx @agents-npm-packages/vibeui add dock --dir ./src/components/ui

Import

tsx
import { Dock } from "@/components/ui/dock"

API

Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.

<Dock>

PropTypeDefaultDescription
items*{ id: string; label: string; icon: ReactNode; onSelect?: (() => void) | undefined; }[]—The items to render.
classNamestring—Extra classes, merged with the component's own.

Source

src/components/ui/dock.tsx
"use client";

import * as React from "react";
import { cn } from "@/lib/utils";

/** macOS-style dock: icons magnify smoothly with distance from the cursor. */
export function Dock({
  items,
  className,
}: {
  items: { id: string; label: string; icon: React.ReactNode; onSelect?: () => void }[];
  className?: string;
}) {
  const refs = React.useRef<(HTMLButtonElement | null)[]>([]);

  const magnify = (clientX: number) => {
    refs.current.forEach((el) => {
      if (!el) return;
      const r = el.getBoundingClientRect();
      const d = Math.abs(clientX - (r.left + r.width / 2));
      const scale = 1 + 0.7 * Math.exp(-((d / 70) ** 2));
      el.style.transform = `scale(${scale}) translateY(${(scale - 1) * -14}px)`;
    });
  };

  const reset = () =>
    refs.current.forEach((el) => {
      if (el) el.style.transform = "";
    });

  return (
    <div
      role="toolbar"
      className={cn(
        "inline-flex items-end gap-2 rounded-xl border border-line bg-surface/80 px-3 py-2.5 shadow-lift backdrop-blur-md",
        className,
      )}
      onMouseMove={(e) => magnify(e.clientX)}
      onMouseLeave={reset}
    >
      {items.map((item, i) => (
        <button
          key={item.id}
          ref={(el) => {
            refs.current[i] = el;
          }}
          type="button"
          aria-label={item.label}
          onClick={item.onSelect}
          className="group/dock relative flex size-11 origin-bottom items-center justify-center rounded-md border border-line bg-surface-muted text-ink shadow-quiet transition-transform duration-150 ease-out will-change-transform"
        >
          {item.icon}
          <span
            role="tooltip"
            className="pointer-events-none absolute -top-9 left-1/2 -translate-x-1/2 whitespace-nowrap rounded-[7px] bg-ink px-2 py-1 text-[11px] font-medium text-surface opacity-0 transition-opacity duration-150 group-hover/dock:opacity-100"
          >
            {item.label}
          </span>
        </button>
      ))}
    </div>
  );
}

Exports Dock. Peer dependencies: clsx, tailwind-merge.

PreviousDither ImageNextDot Globe