Skip to content
Primitives

ScrollArea

Fixed-height overflow surface with a custom HUD scrollbar — a thin graphite gutter and a sharp thumb that arms to signal on hover, rendered identically across browsers (no native OS chrome). Vertical or horizontal.

● LIVE
Active nodes
  • NODE-01
  • NODE-02
  • NODE-03
  • NODE-04
  • NODE-05
  • NODE-06
  • NODE-07
  • NODE-08
  • NODE-09
  • NODE-10
  • NODE-11
  • NODE-12
  • NODE-13
  • NODE-14
  • NODE-15
  • NODE-16
  • NODE-17
  • NODE-18
  • NODE-19
  • NODE-20
  • NODE-21
  • NODE-22
  • NODE-23
  • NODE-24
  • NODE-25
  • NODE-26
  • NODE-27
  • NODE-28
npx shadcn@latest add https://okibi.cndr.dev/r/scroll-area.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/scroll-area.json

Install the required dependencies:

npm install @radix-ui/react-scroll-area

Copy the source from the Code tab above into components/ui/scroll-area.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.

Usage

import { ScrollArea } from "@/components/ui/scroll-area"

<ScrollArea className="h-64 w-56 border border-border-strong">
  <div className="p-4">{/* long content… */}</div>
</ScrollArea>

Anatomy

A ScrollArea renders a focusable viewport around your content plus a ScrollBar. Add a second ScrollBar with orientation='horizontal' for sideways scroll; the horizontal child must be w-max/whitespace-nowrap to overflow.

<ScrollArea>
  {/* content */}
  <ScrollBar orientation="horizontal" />
</ScrollArea>

Examples

Horizontal

Pair the viewport with a <ScrollBar orientation="horizontal" /> to scroll a wide row sideways — a filmstrip of sector tiles, a timeline. The gutter rotates to the bottom edge and runs the same signal-arming thumb along it.

● LIVE
0x4A
Sector 0x4A
0x4B
Sector 0x4B
0x4C
Sector 0x4C
0x4D
Sector 0x4D
0x4E
Sector 0x4E
0x4F
Sector 0x4F
0x50
Sector 0x50
0x51
Sector 0x51
0x52
Sector 0x52
0x53
Sector 0x53
0x54
Sector 0x54
0x55
Sector 0x55

API Reference

ScrollArea

PropTypeDefaultDescription
type"hover" | "scroll" | "auto" | "always""hover"When the scrollbar is shown — hover reveals it on pointer-over, always keeps it visible. Forwarded to Radix.
scrollHideDelaynumber600Delay (ms) before the scrollbar hides after scrolling stops, for hover / scroll. Forwarded to Radix.

ScrollBar

PropTypeDefaultDescription
orientation"vertical" | "horizontal""vertical"Gutter axis. Add a second <ScrollBar orientation="horizontal" /> inside ScrollArea for sideways scrolling.

Accessibility

  • Wraps Radix ScrollArea; the viewport is focusable and keyboard-scrollable, with a square signal focus ring (focus-visible:outline-2) matching the HUD focus language.
  • Radix paints the scrollbar as real DOM elements, so it is keyboard- and pointer-operable and identical across browsers and OSes — no native gutter shift.
  • Use the default type='hover' for an overlay scrollbar; type='always' keeps it visible for surfaces where a persistent affordance matters.
  • Reach for this only on fixed-height panels — for ordinary page overflow, native scroll keeps find-in-page and momentum that this surface does not replace.
  • The Corner is transparent decorative chrome; the thumb arms graphite-to-signal on hover but conveys no state to assistive tech.

Guidelines

Do

  • Constrain the ScrollArea to a fixed height or width — it exists for bounded HUD panels, not the page body.
  • Add a horizontal ScrollBar and give the child w-max when content must scroll sideways.
  • Switch to type='always' when the scrollbar should read as a permanent affordance rather than appearing on hover.

Don't

  • Do not use it as a global scrollbar replacement — defer to the native scrollbar-signal utility for page overflow.
  • Do not nest scroll areas inside scroll areas; pick one bounded viewport per region.
  • Do not expect a horizontal thumb to appear if the child can wrap — it needs whitespace-nowrap/w-max to overflow.

On this page