{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "scroll-progress",
  "title": "Scroll Progress",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "A thin reading-progress bar fixed to the top or bottom of the page, or to a scrollable panel, that fills on a soft spring (or exactly under reduced motion).",
  "dependencies": [
    "motion@^12"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/scroll-progress.tsx",
      "content": "// Ballmac UI: Scroll Progress. https://ui.ballmac.com/components/scroll-progress\n\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useReducedMotion, useScroll, useSpring } from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\ntype ScrollProgressProps = Omit<React.ComponentProps<\"div\">, \"children\"> & {\n  /** Which edge the bar sits on. */\n  position?: \"top\" | \"bottom\";\n  /** A scrollable element to track instead of the page. Give the bar `absolute` positioning inside its wrapper. */\n  container?: React.RefObject<HTMLElement | null>;\n  /** Bar thickness in pixels. */\n  thickness?: number;\n};\n\n/**\n * A thin bar that fills as the reader scrolls. It is decorative (hidden from assistive technology): screen-reader\n * users already have their own position cues. The fill follows a soft spring, or tracks the scroll exactly under reduced motion.\n */\nfunction ScrollProgress({ position = \"top\", container, thickness = 3, className, ...props }: ScrollProgressProps) {\n  const reduce = useReducedMotion();\n  const { scrollYProgress } = useScroll(container ? { container } : undefined);\n  const spring = useSpring(scrollYProgress, { stiffness: 200, damping: 30, restDelta: 0.001 });\n  return (\n    <div\n      aria-hidden=\"true\"\n      data-slot=\"scroll-progress\"\n      style={{ height: thickness }}\n      className={cn(\n        \"pointer-events-none fixed inset-x-0 z-50 bg-transparent\",\n        position === \"top\" ? \"top-0\" : \"bottom-0\",\n        className,\n      )}\n      {...props}\n    >\n      <motion.div\n        className=\"h-full origin-left bg-gradient-to-r from-primary/70 to-primary\"\n        style={{ scaleX: reduce ? scrollYProgress : spring }}\n      />\n    </div>\n  );\n}\n\nexport { ScrollProgress, type ScrollProgressProps };\n",
      "type": "registry:ui",
      "target": "@components/ballmac/scroll-progress.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "progress",
      "reading",
      "scroll",
      "indicator"
    ],
    "version": "1.0.0",
    "updated": "2026-09-30",
    "ai": {
      "summary": "Render <ScrollProgress /> once. Pass container (a ref) to follow a scrollable element and add className='absolute' inside its wrapper.",
      "whenToUse": [
        "Long articles and docs",
        "Multi-section landing pages"
      ],
      "whenNotToUse": [
        "Task or upload progress; use progress",
        "Step indicators; use progress-steps"
      ],
      "composesWith": [
        "table-of-contents",
        "back-to-top"
      ],
      "a11y": [
        {
          "keys": "Screen readers",
          "action": "Decorative (aria-hidden); the page's own scroll position is already exposed"
        },
        {
          "keys": "Reduced motion",
          "action": "No spring smoothing"
        }
      ],
      "customization": [
        "position: top | bottom",
        "thickness",
        "container"
      ]
    },
    "examples": [
      "scroll-progress-demo",
      "scroll-progress-states"
    ],
    "url": "https://ui.ballmac.com/components/scroll-progress"
  },
  "categories": [
    "navigation"
  ],
  "type": "registry:ui"
}