// ~/src/siteplane/components/scroll-area/scroll-area.tsx "use client"; import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"; import type React from "react"; import { cn, type WithStringClassName } from "../../system/utils/cn"; import { baseClass, slotClass } from "../../system/utils/slot-class"; import "./scroll-area.css"; export function ScrollArea({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, className, children, scrollFade = false, scrollbar = "auto", scrollbarGutter = false, fill = false, clampContentMinWidth = true, ...props }: WithStringClassName & { scrollFade?: boolean; scrollbar?: "auto" | "hidden"; scrollbarGutter?: boolean; fill?: boolean; clampContentMinWidth?: boolean; }): React.ReactElement { // The root is presentational; the viewport is what joins the tab order, so // it carries the accessible name as a labelled region. const viewportLabelProps = ariaLabel || ariaLabelledBy ? { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, role: "region", } : undefined; // The root fills its container (design-rework.css); a size utility or a // consumer's own rule replaces either dimension. A keyboard-focused viewport // draws the kit focus outline on the root (design-rework.css), where the // scroll fade's mask cannot clip it. return ( {children} {scrollbar === "auto" ? ( <> ) : null} ); } export function ScrollBar({ className, orientation = "vertical", ...props }: WithStringClassName): React.ReactElement { return ( ); } export { ScrollAreaPrimitive }; // ~/src/siteplane/components/scroll-area/scroll-area.css @layer theme, base, components, siteplane, utilities; @layer siteplane { /* The scrollbar fades in at once while hovered or scrolled. When the pointer * leaves or scrolling stops, it stays for three micro steps (300 ms) and * fades out like a state change. The fade sits at zero specificity, so the * reduced-motion rules below switch it off (M5). */ :where(.sp-scroll-area-scrollbar-motion) { transition-property: opacity; transition-timing-function: var(--motion-ease-state); transition-duration: var(--motion-duration-fast); transition-delay: calc(var(--motion-duration-micro) * 3); } :where( .sp-scroll-area-scrollbar-motion:is([data-hovering], [data-scrolling]) ) { transition-duration: var(--motion-duration-micro); transition-delay: 0ms; } @media (prefers-reduced-motion: reduce) { :where(.sp-scroll-area-scrollbar-motion) { transition-property: none; transition-delay: 0ms; } } html[data-motion-reduce="true"] .sp-scroll-area-scrollbar-motion, [data-sp-motion="reduced"] .sp-scroll-area-scrollbar-motion { transition: none; transition-delay: 0s; } } // ~/src/siteplane/components/scroll-area/index.ts export * from "./scroll-area";