{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-to-top-button",
  "title": "Scroll to Top Button",
  "description": "A floating shortcut that appears after someone moves down a long page or scroll container and returns them to the top.",
  "dependencies": [
    "lucide-react@^1.31.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/scroll-to-top-button/scroll-to-top-button.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ArrowUpToLine } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface ScrollToTopButtonProps extends Omit<\n  React.ButtonHTMLAttributes<HTMLButtonElement>,\n  \"children\"\n> {\n  containerRef?: React.RefObject<HTMLElement | null>\n  container?: HTMLElement | null\n  behavior?: ScrollBehavior\n  label?: string\n  showAfter?: number\n  /** Replaces the default arrow. */\n  icon?: React.ReactNode\n}\n\nexport const ScrollToTopButton = React.forwardRef<\n  HTMLButtonElement,\n  ScrollToTopButtonProps\n>(function ScrollToTopButton(\n  {\n    behavior = \"smooth\",\n    className,\n    container,\n    containerRef,\n    icon,\n    label = \"Scroll to top\",\n    onClick,\n    showAfter = 320,\n    type = \"button\",\n    ...buttonProps\n  },\n  forwardedRef\n) {\n  const [visible, setVisible] = React.useState(false)\n\n  React.useEffect(() => {\n    const scrollContainer = container ?? containerRef?.current\n    const scrollTarget = scrollContainer ?? window\n\n    function updateVisibility() {\n      const scrollTop = scrollContainer?.scrollTop ?? window.scrollY\n      setVisible(scrollTop > showAfter)\n    }\n\n    updateVisibility()\n    scrollTarget.addEventListener(\"scroll\", updateVisibility, { passive: true })\n\n    return () => scrollTarget.removeEventListener(\"scroll\", updateVisibility)\n  }, [container, containerRef, showAfter])\n\n  function scrollToTop(event: React.MouseEvent<HTMLButtonElement>) {\n    onClick?.(event)\n    if (event.defaultPrevented) return\n\n    const reduceMotion = window.matchMedia(\n      \"(prefers-reduced-motion: reduce)\"\n    ).matches\n    const scrollBehavior = reduceMotion ? \"instant\" : behavior\n    const scrollContainer = container ?? containerRef?.current\n\n    if (scrollContainer) {\n      scrollContainer.scrollTo({ top: 0, behavior: scrollBehavior })\n      return\n    }\n\n    window.scrollTo({ top: 0, behavior: scrollBehavior })\n  }\n\n  // Kept in the page so it can fade rather than appear, and made completely\n  // inert while hidden: not clickable, not focusable, not announced.\n  return (\n    <button\n      aria-hidden={!visible || undefined}\n      aria-label={label}\n      data-slot=\"scroll-to-top-button\"\n      data-visible={visible || undefined}\n      tabIndex={visible ? undefined : -1}\n      className={cn(\n        \"group bg-foreground text-background fixed right-6 bottom-6 z-40 inline-flex size-12 items-center justify-center rounded-full shadow-lg transition-[opacity,transform] duration-200 hover:-translate-y-0.5 active:translate-y-0 active:scale-[0.97] motion-reduce:transition-none\",\n        visible\n          ? \"translate-y-0 opacity-100\"\n          : \"pointer-events-none translate-y-2 opacity-0\",\n        className\n      )}\n      onClick={scrollToTop}\n      ref={forwardedRef}\n      title={label}\n      type={type}\n      {...buttonProps}\n    >\n      <span\n        aria-hidden=\"true\"\n        className=\"inline-flex transition-transform duration-150 group-hover:-translate-y-0.5 motion-reduce:transition-none\"\n      >\n        {icon ?? <ArrowUpToLine size={19} strokeWidth={2} />}\n      </span>\n    </button>\n  )\n})\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "button",
    "navigation",
    "scroll",
    "floating"
  ],
  "type": "registry:ui"
}