{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"dock","type":"registry:ui","title":"Dock","description":"Icon dock on a lit glass shelf: icons swell on a smooth cosine curve toward the pointer, a soft pool of light and a brightening edge follow it across the shelf, and a small dot and floor reflection mark the icon underneath.","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["navigation"],"registryDependencies":["https://fancy-ui.rama.app/r/internals.json","https://fancy-ui.rama.app/r/internals-dom.json","https://fancy-ui.rama.app/r/internals-motion.json","https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/dock/Dock.tsx","content":"\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport type { CSSProperties, PointerEvent as ReactPointerEvent, ReactNode } from \"react\";\nimport { cn } from \"../../utils\";\nimport { useMediaQuery, useReducedMotion } from \"../../internals/motion/media-query\";\nimport { DOCK_CONTEXT_KEY } from \"./types\";\nimport type { DataOrientation, Direction, DockContext } from \"./types\";\nimport \"./dock.css\";\n\nexport interface DockProps {\n\t/** Additional CSS classes */\n\tclassName?: string;\n\t/** Maximum size increase in pixels. */\n\tmagnification?: number;\n\t/** Pointer distance over which the magnification falls off. */\n\tdistance?: number;\n\t/** Cross-axis alignment of the icons. */\n\tdirection?: Direction;\n\t/** Dock orientation. */\n\torientation?: DataOrientation;\n\t/** A soft pool of accent light inside the shelf that follows the pointer. */\n\tspotlight?: boolean;\n\t/** A soft contact shadow / glow ellipse under each icon that grows with it. */\n\treflection?: boolean;\n\t/** Accessible name for the toolbar. */\n\tariaLabel?: string;\n\t/** The `DockIcon`s and `DockSeparator`s. */\n\tchildren?: ReactNode;\n}\n\n/**\n * Everything one frame writes, in one piece of state so a frame is one update\n * rather than three. The source keeps the pointer in two `{ current }` boxes\n * and the shelf-local spot in two plain runes; here the boxes are rebuilt from\n * this state on the context below.\n */\ninterface Tracked {\n\t/** Pointer, viewport coordinates; `Infinity` outside the dock. */\n\tx: number;\n\ty: number;\n\t/** Pointer inside the shelf, shelf-local pixels. Written to `--dock-x`/`--dock-y`. */\n\tspotX: number;\n\tspotY: number;\n\thovering: boolean;\n}\n\n/**\n * An icon dock on a lit glass shelf: icons swell on a cosine curve as the\n * pointer approaches, a pool of light follows it across the shelf, and the\n * shelf edge brightens near it.\n *\n * No `ref` prop: the source exposes no bindable ref, and no rest props either —\n * it reads only these props and spreads nothing.\n */\nexport function Dock({\n\tclassName = \"\",\n\tmagnification = 60,\n\tdistance = 140,\n\tdirection = \"middle\",\n\torientation = \"horizontal\",\n\tspotlight = true,\n\treflection = true,\n\tariaLabel,\n\tchildren,\n}: DockProps) {\n\tconst [tracked, setTracked] = useState<Tracked>({\n\t\tx: Infinity,\n\t\ty: Infinity,\n\t\tspotX: 0,\n\t\tspotY: 0,\n\t\thovering: false,\n\t});\n\n\tconst shelf = useRef<HTMLDivElement>(null);\n\n\t// The magnification is a JS-written inline `width`/`height` on each icon, so\n\t// a CSS media query cannot stop it — the driver has to. Neither query is\n\t// read during render or in a lazy initializer: `useMediaQuery` answers\n\t// `false` for the server render and the hydration render and only then goes\n\t// live, which is what keeps this SSR-safe as a one-liner.\n\tconst reduced = useReducedMotion();\n\t// `any-hover`, not `hover`: the unprefixed feature describes only the\n\t// PRIMARY pointing device, so a hybrid laptop-tablet whose primary input is\n\t// touch answers `(hover: none)` even with a mouse plugged in — and the dock\n\t// would then ignore every real mouse move. `any-hover: none` is true only\n\t// when NO attached device can hover, which is the actual question here.\n\t// Touch on such a hybrid is suppressed by `pointerType` below instead.\n\tconst coarse = useMediaQuery(\"(any-hover: none)\");\n\n\t// One flag, two reasons: a visitor who asked for less motion, and a device\n\t// where nothing can hover at all (where the icons under a finger would\n\t// magnify around wherever the last tap happened to land). Either way the\n\t// icons keep their resting 40px.\n\tconst magnify = !reduced && !coarse;\n\n\t// Tracking is wider than magnifying: under reduced motion the pointer is\n\t// still followed so the indicator dot can mark the icon under it — only the\n\t// size change is withheld. With nothing that can hover, nothing is tracked.\n\tconst track = !coarse;\n\n\t// One frame in flight at most: every pointer event overwrites the pending\n\t// coordinates and the frame applies the latest pair. A leave is just a\n\t// pending `Infinity`, so a move and a leave in the same frame can never\n\t// land in the wrong order.\n\tconst frame = useRef(0);\n\tconst pending = useRef({ x: Infinity, y: Infinity });\n\n\tuseEffect(\n\t\t() => () => {\n\t\t\tif (frame.current && typeof cancelAnimationFrame === \"function\") {\n\t\t\t\tcancelAnimationFrame(frame.current);\n\t\t\t}\n\t\t\tframe.current = 0;\n\t\t},\n\t\t[]\n\t);\n\n\tfunction flush() {\n\t\tframe.current = 0;\n\t\tconst { x, y } = pending.current;\n\t\tif (Number.isFinite(x) && Number.isFinite(y)) {\n\t\t\t// Measured here, in the frame, not in the state updater: an updater\n\t\t\t// must stay pure, and the rect is read once per frame either way.\n\t\t\tconst rect = shelf.current?.getBoundingClientRect();\n\t\t\tsetTracked((prev) => ({\n\t\t\t\tx,\n\t\t\t\ty,\n\t\t\t\tspotX: rect ? x - rect.left : prev.spotX,\n\t\t\t\tspotY: rect ? y - rect.top : prev.spotY,\n\t\t\t\thovering: true,\n\t\t\t}));\n\t\t} else {\n\t\t\tsetTracked((prev) => ({ ...prev, x, y, hovering: false }));\n\t\t}\n\t}\n\n\tfunction queue(x: number, y: number) {\n\t\tpending.current = { x, y };\n\t\tif (!frame.current) frame.current = requestAnimationFrame(flush);\n\t}\n\n\tconst context = useMemo<DockContext>(\n\t\t() => ({\n\t\t\tmouseX: { current: tracked.x },\n\t\t\tmouseY: { current: tracked.y },\n\t\t\tmagnification,\n\t\t\tdistance,\n\t\t\torientation,\n\t\t\tmagnify,\n\t\t\treflection,\n\t\t}),\n\t\t[tracked.x, tracked.y, magnification, distance, orientation, magnify, reflection]\n\t);\n\n\t// Pointer events, not mouse events, for one reason: `pointerType`. A tap\n\t// synthesises a `mousemove` indistinguishable from a real one, so on a\n\t// device that CAN hover but is currently being touched, the mouse-event\n\t// version magnified around the last tap. Non-primary pointers are dropped\n\t// too — a second finger has no business moving the magnifier.\n\t//\n\t// clientX/clientY, not pageX/pageY: `DockIcon` measures itself with\n\t// `getBoundingClientRect()`, which is relative to the VIEWPORT. Page\n\t// coordinates add the scroll offset, so on a scrolled page every distance\n\t// was off by exactly that offset and the magnifier swelled somewhere the\n\t// pointer was not.\n\tfunction onPointerMove(e: ReactPointerEvent<HTMLDivElement>) {\n\t\tif (!track) return;\n\t\tif (e.pointerType === \"touch\" || !e.isPrimary) return;\n\t\tqueue(e.clientX, e.clientY);\n\t}\n\n\t// Deliberately ungated, unlike `onPointerMove`: if the preference or the\n\t// pointer type flips while a pointer is already inside the dock, the last\n\t// tracked position would otherwise stay stuck in `mouseX`/`mouseY` forever.\n\t// Resetting to Infinity is what returns every icon to its resting size.\n\tfunction onPointerLeave() {\n\t\tqueue(Infinity, Infinity);\n\t}\n\n\tconst directionClass =\n\t\tdirection === \"top\" ? \"items-start\" : direction === \"bottom\" ? \"items-end\" : \"items-center\";\n\n\tconst style = {\n\t\t\"--dock-x\": `${tracked.spotX}px`,\n\t\t\"--dock-y\": `${tracked.spotY}px`,\n\t} as CSSProperties;\n\n\treturn (\n\t\t<DOCK_CONTEXT_KEY.Provider value={context}>\n\t\t\t<div\n\t\t\t\tref={shelf}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"dock-shelf relative isolate mx-auto flex h-[58px] w-max gap-3 rounded-2xl border p-2 backdrop-blur-md backdrop-saturate-150\",\n\t\t\t\t\t\"border-black/[0.08] bg-white/75 shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_1px_2px_rgba(0,0,0,0.05),0_14px_32px_-14px_rgba(0,0,0,0.22)]\",\n\t\t\t\t\t\"dark:border-white/[0.08] dark:bg-[rgba(20,20,22,0.7)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.08),0_1px_0_rgba(0,0,0,0.5),0_18px_40px_-14px_rgba(0,0,0,0.8)]\",\n\t\t\t\t\torientation === \"vertical\" && \"h-max w-[58px] flex-col\",\n\t\t\t\t\tdirectionClass,\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tstyle={style}\n\t\t\t\tdata-orientation={orientation}\n\t\t\t\tdata-spotlight={spotlight || undefined}\n\t\t\t\tdata-reflection={reflection || undefined}\n\t\t\t\tdata-hover={tracked.hovering || undefined}\n\t\t\t\tonPointerMove={onPointerMove}\n\t\t\t\tonPointerLeave={onPointerLeave}\n\t\t\t\trole=\"toolbar\"\n\t\t\t\taria-label={ariaLabel}\n\t\t\t\taria-orientation={orientation}\n\t\t\t\ttabIndex={0}\n\t\t\t>\n\t\t\t\t<span className=\"dock-deco\" aria-hidden=\"true\">\n\t\t\t\t\t{spotlight && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<span className=\"dock-spot\" />\n\t\t\t\t\t\t\t<span className=\"dock-rim\">\n\t\t\t\t\t\t\t\t<span className=\"dock-rim-light\" />\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t\t<span className=\"dock-frame\" />\n\t\t\t\t</span>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</DOCK_CONTEXT_KEY.Provider>\n\t);\n}\n","type":"registry:ui","target":"@ui/fancy/components/dock/Dock.tsx"},{"path":"react/src/components/dock/DockIcon.tsx","content":"\"use client\";\n\nimport type { ReactNode } from \"react\";\nimport { useElementRef } from \"../../internals/dom/use-element-ref\";\nimport { useDockContext } from \"./types\";\nimport \"./dock-icon.css\";\n\n/** Resting edge length of every icon, in pixels. */\nexport const DOCK_BASE_SIZE = 40;\n\n/**\n * The magnification curve: a cosine bell rather than a straight line.\n * Full `magnification` under the pointer, easing smoothly to nothing at\n * `distance`, with a flat top and flat shoulders — so neighbours swell and\n * settle instead of forming a tent. Pure, so every framework shares the exact\n * numbers.\n */\nexport function dockIconSize(offset: number, magnification: number, distance: number): number {\n\tif (!distance || !magnification || !Number.isFinite(offset)) return DOCK_BASE_SIZE;\n\tconst t = Math.min(Math.max(Math.abs(offset) / distance, 0), 1);\n\treturn DOCK_BASE_SIZE + magnification * 0.5 * (1 + Math.cos(Math.PI * t));\n}\n\nexport interface DockIconProps {\n\t/** Additional CSS classes */\n\tclassName?: string;\n\t/** The icon's content. */\n\tchildren?: ReactNode;\n}\n\n/**\n * One item in a `Dock`, sized from its distance to the pointer, with a floor\n * reflection and an indicator dot when it is the icon under the pointer.\n *\n * The node arrives through `useElementRef` rather than a plain `useRef`: the\n * size is computed while rendering, so the component has to re-render once the\n * element actually exists. Before it does, the offset is `Infinity` and the\n * icon renders at its resting 40px — which is also the size it has at mount,\n * with the pointer still at `Infinity`, so nothing moves on that extra render.\n */\nexport function DockIcon({ className = \"\", children }: DockIconProps) {\n\tconst context = useDockContext();\n\n\tconst [node, iconRef] = useElementRef<HTMLDivElement>();\n\n\t// Signed offset from the icon's centre to the pointer along the dock's\n\t// main axis, plus the icon's own half-extent on that axis. Both in viewport\n\t// coordinates, the same frame `Dock` records the pointer in.\n\tfunction measure(): { offset: number; half: number } {\n\t\tconst vertical = context.orientation === \"vertical\";\n\t\tconst pointer = vertical ? context.mouseY.current : context.mouseX.current;\n\t\t// Pointer outside the dock (or never tracked, as on a device that\n\t\t// cannot hover): skip the `getBoundingClientRect()` entirely.\n\t\tif (!node || !Number.isFinite(pointer)) return { offset: Infinity, half: 0 };\n\n\t\tconst bounds = node.getBoundingClientRect();\n\t\treturn vertical\n\t\t\t? { offset: pointer - bounds.y - bounds.height / 2, half: bounds.height / 2 }\n\t\t\t: { offset: pointer - bounds.x - bounds.width / 2, half: bounds.width / 2 };\n\t}\n\n\tconst probe = measure();\n\n\tconst iconSize = context.magnify\n\t\t? dockIconSize(probe.offset, context.magnification, context.distance)\n\t\t: DOCK_BASE_SIZE;\n\n\t// The icon the pointer is over (within its own half-extent) gets the\n\t// indicator dot. Works under reduced motion too — only the size is frozen.\n\tconst active = Math.abs(probe.offset) <= probe.half && probe.half > 0;\n\n\treturn (\n\t\t<div\n\t\t\tref={iconRef}\n\t\t\tclassName={`dock-icon relative z-[1] flex aspect-square cursor-pointer items-center justify-center rounded-full ${className}`}\n\t\t\tstyle={{ width: `${iconSize}px`, height: `${iconSize}px` }}\n\t\t\tdata-orientation={context.orientation}\n\t\t\tdata-reflection={context.reflection || undefined}\n\t\t\tdata-dock-active={active || undefined}\n\t\t>\n\t\t\t{children}\n\t\t</div>\n\t);\n}\n","type":"registry:ui","target":"@ui/fancy/components/dock/DockIcon.tsx"},{"path":"react/src/components/dock/DockSeparator.tsx","content":"\"use client\";\n\nimport { cn } from \"../../utils\";\nimport { useDockContext } from \"./types\";\n\nexport interface DockSeparatorProps {\n\t/** Additional CSS classes */\n\tclassName?: string;\n}\n\n/**\n * A hairline that fades out at both ends, so it reads as a seam in the glass\n * rather than a bar laid on top of it. Laid across the dock's own axis.\n */\nexport function DockSeparator({ className = \"\" }: DockSeparatorProps) {\n\tconst context = useDockContext();\n\n\treturn (\n\t\t<div\n\t\t\trole=\"separator\"\n\t\t\taria-orientation={context.orientation === \"vertical\" ? \"horizontal\" : \"vertical\"}\n\t\t\tclassName={cn(\n\t\t\t\t\"relative z-[1] block shrink-0 self-center\",\n\t\t\t\tcontext.orientation === \"vertical\"\n\t\t\t\t\t? \"h-px w-4/5 bg-gradient-to-r\"\n\t\t\t\t\t: \"h-4/5 w-px bg-gradient-to-b\",\n\t\t\t\t\"from-transparent via-black/15 to-transparent dark:via-white/15\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t/>\n\t);\n}\n","type":"registry:ui","target":"@ui/fancy/components/dock/DockSeparator.tsx"},{"path":"react/src/components/dock/dock-icon.css","content":"/*\n * Dock icon. Mirrors the Svelte <style> block of DockIcon.svelte rule for\n * rule, anchored on the source's own root class `.dock-icon`.\n */\n.dock-icon {\n\t--_dock-accent: var(--dock-accent, var(--primary, #71717a));\n\t--_dock-ease: var(--ft-ease-out, cubic-bezier(0.16, 1, 0.3, 1));\n}\n\n/* Indicator dot: below the icon on a horizontal dock, beside it on a\n   vertical one. Hidden until the icon is the one under the pointer. */\n.dock-icon::before {\n\tcontent: \"\";\n\tposition: absolute;\n\tleft: 50%;\n\ttop: calc(100% + 4px);\n\twidth: 4px;\n\theight: 4px;\n\tmargin-left: -2px;\n\tborder-radius: 9999px;\n\tbackground: var(--_dock-accent);\n\topacity: 0;\n\tpointer-events: none;\n}\n.dark .dock-icon::before {\n\tbackground: color-mix(in oklab, var(--_dock-accent) 45%, white);\n\tbox-shadow: 0 0 6px 1px color-mix(in oklab, var(--_dock-accent) 55%, transparent);\n}\n.dock-icon[data-orientation=\"vertical\"]::before {\n\tleft: auto;\n\tright: calc(100% + 4px);\n\ttop: 50%;\n\tmargin-left: 0;\n\tmargin-top: -2px;\n}\n.dock-icon[data-dock-active]::before {\n\topacity: 1;\n}\n\n/* Floor reflection: a soft ellipse under the icon — a contact shadow on\n   the light shelf, a faint glow on the dark one. Sized in percent, so it\n   grows with the magnified icon for free. */\n.dock-icon[data-reflection]::after {\n\tcontent: \"\";\n\tposition: absolute;\n\tleft: 14%;\n\tright: 14%;\n\ttop: calc(100% - 4px);\n\theight: 22%;\n\tborder-radius: 50%;\n\tbackground: radial-gradient(closest-side, rgba(0, 0, 0, 0.22), transparent);\n\tfilter: blur(3px);\n\topacity: 0.7;\n\tz-index: -1;\n\tpointer-events: none;\n}\n.dark .dock-icon[data-reflection]::after {\n\tbackground: radial-gradient(\n\t\tclosest-side,\n\t\tcolor-mix(in oklab, var(--_dock-accent) 26%, transparent),\n\t\ttransparent\n\t);\n}\n.dock-icon[data-reflection][data-dock-active]::after {\n\topacity: 1;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n\t.dock-icon {\n\t\ttransition:\n\t\t\twidth 180ms var(--_dock-ease),\n\t\t\theight 180ms var(--_dock-ease);\n\t}\n\t.dock-icon::before,\n\t.dock-icon[data-reflection]::after {\n\t\ttransition: opacity 180ms var(--_dock-ease);\n\t}\n}\n","type":"registry:ui","target":"@ui/fancy/components/dock/dock-icon.css"},{"path":"react/src/components/dock/dock.css","content":"/*\n * Dock shelf. Mirrors the Svelte <style> block of Dock.svelte rule for rule.\n * The Svelte compiler scoped every selector; here each one is anchored on the\n * source's own root class `.dock-shelf`, so no generic name leaks into the page.\n */\n\n/* Theme hooks. Every private var reads a public one first, so a consumer\n   can recolour the light from a class without touching this file. */\n.dock-shelf {\n\t--_dock-accent: var(--dock-accent, var(--primary, #71717a));\n\t--_dock-spot-size: var(--dock-spotlight-size, 140px);\n\t--_dock-frame: var(--dock-frame-color, rgba(0, 0, 0, 0.05));\n\t--_dock-core: color-mix(in oklab, var(--_dock-accent) 45%, white);\n\t--_dock-ease: var(--ft-ease-out, cubic-bezier(0.16, 1, 0.3, 1));\n\toutline: none;\n}\n.dark .dock-shelf {\n\t--_dock-frame: var(--dock-frame-color, rgba(255, 255, 255, 0.045));\n}\n\n.dock-shelf:focus-visible {\n\toutline: 2px solid color-mix(in oklab, var(--_dock-accent) 55%, transparent);\n\toutline-offset: 3px;\n}\n\n/* Decorative stack: clipped to the shelf, painted under the icons. The\n   icons are not clipped — they are siblings, above this layer. */\n.dock-shelf .dock-deco {\n\tposition: absolute;\n\tinset: -1px;\n\tz-index: 0;\n\toverflow: hidden;\n\tborder-radius: inherit;\n\tpointer-events: none;\n}\n\n/* The nested inner frame: a second hairline just inside the shelf edge,\n   with a faint top-lit fill. */\n.dock-shelf .dock-frame {\n\tposition: absolute;\n\tinset: 4px;\n\tborder: 1px solid var(--_dock-frame);\n\tborder-radius: calc(1rem - 4px);\n\tbackground: linear-gradient(to bottom, rgba(255, 255, 255, 0.5), transparent 60%);\n}\n.dark .dock-shelf .dock-frame {\n\tbackground: linear-gradient(to bottom, rgba(255, 255, 255, 0.025), transparent 60%);\n}\n\n/* The spotlight: a blurred pool of accent light with a white-hot core,\n   moved by transform only. Light theme lays a thin tint of the accent\n   into the glass; dark theme screens light onto it. */\n.dock-shelf .dock-spot {\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\twidth: var(--_dock-spot-size);\n\theight: var(--_dock-spot-size);\n\tborder-radius: 50%;\n\tbackground: radial-gradient(\n\t\tclosest-side,\n\t\tcolor-mix(in oklab, var(--_dock-accent) 20%, transparent) 0%,\n\t\tcolor-mix(in oklab, var(--_dock-accent) 8%, transparent) 45%,\n\t\ttransparent 100%\n\t);\n\tfilter: blur(10px);\n\topacity: 0;\n\ttransform: translate(calc(var(--dock-x) - 50%), calc(var(--dock-y) - 50%));\n\twill-change: transform, opacity;\n}\n.dark .dock-shelf .dock-spot {\n\tbackground: radial-gradient(\n\t\tclosest-side,\n\t\tcolor-mix(in oklab, var(--_dock-core) 50%, transparent) 0%,\n\t\tcolor-mix(in oklab, var(--_dock-accent) 20%, transparent) 45%,\n\t\ttransparent 100%\n\t);\n\tmix-blend-mode: screen;\n}\n\n/* The lit rim: a 1px ring (padding + mask) holding one blurred blob that\n   rides the same transform, so the shelf edge brightens only near the\n   pointer. */\n.dock-shelf .dock-rim {\n\tposition: absolute;\n\tinset: 0;\n\tpadding: 1px;\n\tborder-radius: inherit;\n\t-webkit-mask:\n\t\tlinear-gradient(#000 0 0) content-box,\n\t\tlinear-gradient(#000 0 0);\n\t-webkit-mask-composite: xor;\n\tmask:\n\t\tlinear-gradient(#000 0 0) content-box exclude,\n\t\tlinear-gradient(#000 0 0);\n\topacity: 0;\n}\n.dock-shelf .dock-rim-light {\n\tposition: absolute;\n\tleft: 0;\n\ttop: 0;\n\twidth: 220px;\n\theight: 96px;\n\tborder-radius: 50%;\n\tbackground: radial-gradient(\n\t\tclosest-side,\n\t\tcolor-mix(in oklab, var(--_dock-accent) 70%, transparent),\n\t\ttransparent\n\t);\n\ttransform: translate(calc(var(--dock-x) - 50%), calc(var(--dock-y) - 50%));\n}\n.dark .dock-shelf .dock-rim-light {\n\tbackground: radial-gradient(closest-side, var(--_dock-core), transparent);\n}\n\n.dock-shelf[data-hover] .dock-spot {\n\topacity: 1;\n}\n.dock-shelf[data-hover] .dock-rim {\n\topacity: 0.9;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n\t.dock-shelf .dock-spot {\n\t\ttransition:\n\t\t\ttransform 140ms var(--_dock-ease),\n\t\t\topacity 300ms var(--_dock-ease);\n\t}\n\t.dock-shelf .dock-rim-light {\n\t\ttransition: transform 140ms var(--_dock-ease);\n\t}\n\t.dock-shelf .dock-rim {\n\t\ttransition: opacity 300ms var(--_dock-ease);\n\t}\n}\n\n/* Reduced motion: the light holds still, dim and centred, whether or not a\n   pointer is over the dock — the same composition as a resting frame. */\n@media (prefers-reduced-motion: reduce) {\n\t.dock-shelf .dock-spot,\n\t.dock-shelf[data-hover] .dock-spot {\n\t\tleft: 50%;\n\t\ttop: 50%;\n\t\ttransform: translate(-50%, -50%);\n\t\topacity: 0.55;\n\t}\n\t.dock-shelf .dock-rim,\n\t.dock-shelf[data-hover] .dock-rim {\n\t\topacity: 0;\n\t}\n}\n\n/* Nothing can hover: no light to follow. */\n@media (any-hover: none) {\n\t.dock-shelf .dock-spot,\n\t.dock-shelf .dock-rim {\n\t\tdisplay: none;\n\t}\n}\n","type":"registry:ui","target":"@ui/fancy/components/dock/dock.css"},{"path":"react/src/components/dock/index.ts","content":"\"use client\";\n\nexport { Dock } from \"./Dock\";\nexport type { DockProps } from \"./Dock\";\nexport { DockIcon } from \"./DockIcon\";\nexport type { DockIconProps } from \"./DockIcon\";\nexport { DockSeparator } from \"./DockSeparator\";\nexport type { DockSeparatorProps } from \"./DockSeparator\";\nexport type { DataOrientation, Direction, DockContext } from \"./types\";\n","type":"registry:ui","target":"@ui/fancy/components/dock/index.ts"},{"path":"react/src/components/dock/types.ts","content":"\"use client\";\n\nimport { createContext, useContext } from \"react\";\n\nexport type DataOrientation = \"vertical\" | \"horizontal\";\nexport type Direction = \"top\" | \"middle\" | \"bottom\";\n\nexport interface DockContext {\n\t/**\n\t * The pointer's VIEWPORT X (`clientX`), `Infinity` when the pointer is\n\t * outside the dock. Viewport, not page, because `DockIcon` measures itself\n\t * with `getBoundingClientRect()`, which is viewport-relative. In a box\n\t * rather than as a bare number — the shape the source publishes, kept\n\t * verbatim so the exported type is unchanged.\n\t *\n\t * There the box exists so children can read a reactive update through a\n\t * stable object; here the box is REBUILT by `Dock` on every pointer frame,\n\t * because a mutated object is invisible to React and the new identity is\n\t * exactly what re-renders the icons. Only `Dock` ever writes it.\n\t */\n\tmouseX: { current: number };\n\t/** The pointer's viewport Y (`clientY`). Same box treatment as `mouseX`. */\n\tmouseY: { current: number };\n\tmagnification: number;\n\tdistance: number;\n\torientation: DataOrientation;\n\t/**\n\t * False when the visitor asked for reduced motion, or when the device has no\n\t * real pointer to track. `Dock` owns the two media queries behind it and\n\t * `DockIcon` reads it before sizing itself. Read-only: only `Dock` may\n\t * write it.\n\t */\n\treadonly magnify: boolean;\n\t/**\n\t * Whether each icon casts its soft floor reflection (the contact shadow /\n\t * glow ellipse under it). Mirrors the `reflection` prop on `Dock`.\n\t */\n\treadonly reflection: boolean;\n}\n\n/**\n * The context `DockIcon` and `DockSeparator` read to find their dock. The\n * source publishes it under a `Symbol` context key; React's own context object\n * plays that role here, so the name is kept and the value is a `React.Context`\n * rather than a symbol.\n *\n * Not re-exported from `index.ts`, because the source's barrel does not export\n * its key either.\n */\nexport const DOCK_CONTEXT_KEY = createContext<DockContext | undefined>(undefined);\nDOCK_CONTEXT_KEY.displayName = \"DockContext\";\n\n/**\n * Required, not optional: both children read `context.orientation` with no\n * guard, so one rendered outside a `Dock` throws either way. A named error says\n * what went wrong instead of leaving a bare property-of-undefined TypeError.\n */\nexport function useDockContext(): DockContext {\n\tconst context = useContext(DOCK_CONTEXT_KEY);\n\tif (context === undefined) {\n\t\tthrow new Error(\"DockContext is missing: this component must be rendered inside a <Dock>.\");\n\t}\n\treturn context;\n}\n","type":"registry:ui","target":"@ui/fancy/components/dock/types.ts"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/dock?framework=react"}}