{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "scroll-area", "title": "Scroll Area (Base UI)", "description": "Scroll container with a restyled overlay scrollbar that stays discoverable on hover and follows the shape system. Falls back to native overflow scrolling on touch-primary devices. Base UI flavor; adapted from Lina (https://lina.sameer.sh).", "dependencies": [ "@base-ui/react" ], "registryDependencies": [ "https://zeron-ui.vercel.app/r/surfaces.json", "utils", "https://zeron-ui.vercel.app/r/shape-context.json", "https://zeron-ui.vercel.app/r/use-touch-primary.json" ], "files": [ { "path": "src/components/ui/scroll-area.tsx", "content": "\"use client\";\n\n// Scroll area built on Base UI — shape-system scrollbar, native overflow\n// fallback on touch-primary devices. Scrollbar\n// machinery adapted from Lina by SameerJS6 (https://lina.sameer.sh); built on\n// @base-ui/react/scroll-area, whose scrollbars stay mounted while scrollable\n// and expose hover/scroll state as data attributes instead of a JS-driven\n// show/hide presence animation.\n\nimport {\n createContext,\n forwardRef,\n useContext,\n type ComponentPropsWithoutRef,\n type ComponentRef,\n} from \"react\";\nimport { ScrollArea as ScrollAreaPrimitive } from \"@base-ui/react/scroll-area\";\nimport { cn } from \"@/lib/utils\";\nimport { useShape } from \"@/lib/shape-context\";\nimport { useTouchPrimary } from \"@/hooks/use-touch-primary\";\n\n// On touch-primary devices the Base UI machinery is skipped entirely in\n// favour of native overflow scrolling (better physics, momentum,\n// rubber-banding); the context lets the exported ScrollBar no-op there.\nconst ScrollAreaContext = createContext(false);\n\ntype Orientation = \"vertical\" | \"horizontal\" | \"both\";\n\ninterface ScrollAreaProps extends ComponentPropsWithoutRef<\"div\"> {\n viewportClassName?: string;\n /** Which axes get scrollbars. Defaults to `\"vertical\"`. */\n orientation?: Orientation;\n}\n\nconst ScrollArea = forwardRef<\n ComponentRef,\n ScrollAreaProps\n>(\n (\n {\n className,\n children,\n viewportClassName,\n orientation = \"vertical\",\n ...props\n },\n ref\n ) => {\n const isTouch = useTouchPrimary();\n\n return (\n \n {isTouch ? (\n \n \n {children}\n \n \n ) : (\n \n \n {/* Content gives Base UI an intrinsic size to measure\n horizontal overflow against. */}\n \n {children}\n \n \n {orientation !== \"horizontal\" && }\n {orientation !== \"vertical\" && }\n {orientation === \"both\" && }\n \n )}\n \n );\n }\n);\n\nScrollArea.displayName = \"ScrollArea\";\n\nconst ScrollBar = forwardRef<\n ComponentRef,\n ComponentPropsWithoutRef\n>(({ className, orientation = \"vertical\", ...props }, ref) => {\n const isTouch = useContext(ScrollAreaContext);\n const shape = useShape();\n\n if (isTouch) return null;\n\n return (\n \n \n \n );\n});\n\nScrollBar.displayName = \"ScrollBar\";\n\nexport { ScrollArea, ScrollBar };\nexport type { ScrollAreaProps };\n", "type": "registry:ui", "target": "components/ui/scroll-area.tsx" } ], "type": "registry:ui" }