Dock
Dock estilo macOS con magnético por proximidad: los íconos crecen según la posición del mouse.
@/components/ui/dock1"use client";2import React, { useRef } from "react";3import { cva } from "class-variance-authority";4import { motion, useMotionValue, useSpring, useTransform } from "motion/react";5 6import { cn } from "@/lib/utils"7 8const DEFAULT_SIZE = 409const DEFAULT_MAGNIFICATION = 6010const DEFAULT_DISTANCE = 14011const DEFAULT_DISABLEMAGNIFICATION = false12 13const dockVariants = cva(14 "supports-backdrop-blur:bg-white/10 supports-backdrop-blur:dark:bg-black/10 mx-auto mt-8 flex h-[58px] w-max items-center justify-center gap-2 rounded-2xl border p-2 backdrop-blur-md"15)16 17const Dock = React.forwardRef((18 {19 className,20 children,21 iconSize = DEFAULT_SIZE,22 iconMagnification = DEFAULT_MAGNIFICATION,23 disableMagnification = DEFAULT_DISABLEMAGNIFICATION,24 iconDistance = DEFAULT_DISTANCE,25 direction = "middle",26 ...props27 },28 ref29) => {30 const mouseX = useMotionValue(Infinity)31 32 const renderChildren = () => {33 return React.Children.map(children, (child) => {34 if (35 React.isValidElement(child) &&36 child.type === DockIcon37 ) {38 return React.cloneElement(child, {39 ...child.props,40 mouseX: mouseX,41 size: iconSize,42 magnification: iconMagnification,43 disableMagnification: disableMagnification,44 distance: iconDistance,45 });46 }47 return child48 });49 }50 51 return (52 <motion.div53 ref={ref}54 onMouseMove={(e) => mouseX.set(e.pageX)}55 onMouseLeave={() => mouseX.set(Infinity)}56 {...props}57 className={cn(dockVariants({ className }), {58 "items-start": direction === "top",59 "items-center": direction === "middle",60 "items-end": direction === "bottom",61 })}>62 {renderChildren()}63 </motion.div>64 );65})66 67Dock.displayName = "Dock"68 69const DockIcon = ({70 size = DEFAULT_SIZE,71 magnification = DEFAULT_MAGNIFICATION,72 disableMagnification,73 distance = DEFAULT_DISTANCE,74 mouseX,75 className,76 children,77 ...props78}) => {79 const ref = useRef(null)80 const padding = Math.max(6, size * 0.2)81 const defaultMouseX = useMotionValue(Infinity)82 83 const distanceCalc = useTransform(mouseX ?? defaultMouseX, (val) => {84 const bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }85 return val - bounds.x - bounds.width / 286 })87 88 const targetSize = disableMagnification ? size : magnification89 90 const sizeTransform = useTransform(distanceCalc, [-distance, 0, distance], [size, targetSize, size])91 92 const scaleSize = useSpring(sizeTransform, {93 mass: 0.1,94 stiffness: 150,95 damping: 12,96 })97 98 return (99 <motion.div100 ref={ref}101 style={{ width: scaleSize, height: scaleSize, padding }}102 className={cn(103 "flex aspect-square cursor-pointer items-center justify-center rounded-full",104 disableMagnification && "hover:bg-muted-foreground transition-colors",105 className106 )}107 {...props}>108 <div>{children}</div>109 </motion.div>110 );111}112 113DockIcon.displayName = "DockIcon"114 115export { Dock, DockIcon, dockVariants }116 Uso
Props
Intro
El componente mide el progreso del contenedor y lo suaviza con spring.
Instalación
Cada sección con id se vuelve un marcador sobre el riel.
Uso
Click en un marcador (o en el track) salta directo a esa sección.
API
El label flotante sigue al thumb y muestra el título activo.
Ejemplo
Al llegar al final, el thumb y los marcadores hacen un pulso.
Acciones del componente
Search for a command to run...
Software a medida, IA integrada y ciberseguridad avanzada para empresas.
Consultoría SEO Avanzado & Analítica
Auditoría técnica profunda para posicionar tu negocio en los primeros resultados de Google.
SEO TécnicoGA4Páginas Web Corporativas Complejas
Plataformas empresariales masivas con múltiples secciones y animaciones fluidas.
Framer MotionMulti-pageIntegración de Inteligencia Artificial en Negocios
Automatizamos la atención, las reservas y las ventas de tu negocio con agentes de IA.
AI AgentsChatbots