Scroll Area
fluid-functionalism/scroll-areaFreeComponent
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. Built on Radix UI Scroll Area; adapted from Lina (https://lina.sameer.sh).
Install it
npx shadcn@latest add https://fluidfunctionalism.com/r/scroll-area.jsonSource
1"use client";23// Adapted from Lina by SameerJS6 (https://lina.sameer.sh) — lina-radix4// scroll-area. Changes from the original: Lina's gradient-only ScrollMask is5// dropped, the scrollbar is restyled to the shape system,6// and tw-animate-css visibility classes are swapped for a plain opacity7// transition. Falls back to native overflow scrolling on touch-primary devices.89import {10 createContext,11 forwardRef,12 useContext,13 useEffect,14 useRef,15 useState,16 type ComponentPropsWithoutRef,17 type ComponentRef,18} from "react";19import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";20import { cn } from "@/lib/utils";21import { useShape } from "@/lib/shape-context";22import { useTouchPrimary } from "@/hooks/use-touch-primary";2324// On touch-primary devices the Radix machinery is skipped entirely in favour25// of native overflow scrolling (better physics, momentum, rubber-banding);26// the context lets the exported ScrollBar no-op in that branch.27const ScrollAreaContext = createContext<boolean>(false);2829// Whether the scrollbar should currently be shown (hovering the area or30// actively scrolling). Owned by ScrollArea rather than Radix's type="hover"31// machinery: Radix only reveals on hover (never on scroll) and stacks32// conditional Presence layers that fight33// the CSS fade. Default true so a standalone ScrollBar stays visible.34const ScrollbarVisibleContext = createContext<boolean>(true);3536// How long the scrollbar lingers after the last scroll event before fading.37// Hover hide is38// immediate — the fade classes add their own 160ms grace.39const SCROLL_LINGER_MS = 600;4041type Orientation = "vertical" | "horizontal" | "both";4243interface ScrollAreaProps44 extends ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> {45 viewportClassName?: string;46 /** Which axes get scrollbars. Defaults to `"vertical"`. */47 orientation?: Orientation;48}4950const ScrollArea = forwardRef<51 ComponentRef<typeof ScrollAreaPrimitive.Root>,52 ScrollAreaProps53>(54 (55 {56 className,57 children,58 // Swallowed: hide timing is owned by the fade classes + scroll linger59 // below, not Radix's hover machinery (see type="always" note).60 scrollHideDelay: _scrollHideDelay,61 viewportClassName,62 orientation = "vertical",63 ...props64 },65 ref66 ) => {67 const isTouch = useTouchPrimary();6869 // Hover + scroll visibility, driving the scrollbar fade below. Scroll70 // reveals linger SCROLL_LINGER_MS after the last scroll event.71// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from fluid-functionalism
All 53 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | fluid-functionalism | Components | Free | 3 deps | |
| Accordion (Base UI)accordion-base | fluid-functionalism | Components | Free | 3 deps | |
| Ask User Questionsask-user-questions | fluid-functionalism | Components | Free | 2 deps | |
| Badgebadge | fluid-functionalism | Components | Free | 1 dep | |
| Buttonbutton | fluid-functionalism | Components | Free | 4 deps | |
| Button (Base UI)button-base | fluid-functionalism | Components | Free | 4 deps | |
| Cardcard | fluid-functionalism | Components | Free | 1 dep | |
| Chat Messagechat-message | fluid-functionalism | Components | Free | 1 dep |
