Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Scroll Area

Augments native scroll functionality for custom, cross-browser styling.

Playground

Tags

v1.2.0-beta.50
v1.2.0-beta.49
v1.2.0-beta.48
v1.2.0-beta.47
v1.2.0-beta.46
v1.2.0-beta.45
v1.2.0-beta.44
v1.2.0-beta.43
v1.2.0-beta.42
v1.2.0-beta.41
v1.2.0-beta.40
v1.2.0-beta.39
v1.2.0-beta.38
v1.2.0-beta.37
v1.2.0-beta.36
v1.2.0-beta.35
v1.2.0-beta.34
v1.2.0-beta.33
v1.2.0-beta.32
v1.2.0-beta.31
v1.2.0-beta.30
v1.2.0-beta.29
v1.2.0-beta.28
v1.2.0-beta.27
v1.2.0-beta.26
v1.2.0-beta.25
v1.2.0-beta.24
v1.2.0-beta.23
v1.2.0-beta.22
v1.2.0-beta.21
v1.2.0-beta.20
v1.2.0-beta.19
v1.2.0-beta.18
v1.2.0-beta.17
v1.2.0-beta.16
v1.2.0-beta.15
v1.2.0-beta.14
v1.2.0-beta.13
v1.2.0-beta.12
v1.2.0-beta.11
v1.2.0-beta.10
v1.2.0-beta.9
v1.2.0-beta.8
v1.2.0-beta.7
v1.2.0-beta.6
v1.2.0-beta.5
v1.2.0-beta.4
v1.2.0-beta.3
v1.2.0-beta.2
v1.2.0-beta.1
—orientation
—type
—scrollHideDelay
—dir
Usage
ScrollArea bundles the Radix Root, Viewport, a vertical scrollbar and the corner — wrap the overflowing content and give the area an explicit size via className. Scrolling stays native; only the scrollbar is custom, styled with the border token so it sits on any surface. The separately exported ScrollBar is only needed for horizontal scrolling.

Tags

v1.2.0-beta.50
v1.2.0-beta.49
v1.2.0-beta.48
v1.2.0-beta.47
v1.2.0-beta.46
v1.2.0-beta.45
v1.2.0-beta.44
v1.2.0-beta.43
v1.2.0-beta.42
v1.2.0-beta.41
v1.2.0-beta.40
v1.2.0-beta.39
v1.2.0-beta.38
v1.2.0-beta.37
v1.2.0-beta.36
v1.2.0-beta.35
v1.2.0-beta.34
v1.2.0-beta.33
v1.2.0-beta.32
v1.2.0-beta.31
v1.2.0-beta.30
v1.2.0-beta.29
v1.2.0-beta.28
v1.2.0-beta.27
v1.2.0-beta.26
v1.2.0-beta.25
v1.2.0-beta.24
v1.2.0-beta.23
v1.2.0-beta.22
v1.2.0-beta.21
v1.2.0-beta.20
v1.2.0-beta.19
v1.2.0-beta.18
v1.2.0-beta.17
v1.2.0-beta.16
v1.2.0-beta.15
v1.2.0-beta.14
v1.2.0-beta.13
v1.2.0-beta.12
v1.2.0-beta.11
v1.2.0-beta.10
v1.2.0-beta.9
v1.2.0-beta.8
v1.2.0-beta.7
v1.2.0-beta.6
v1.2.0-beta.5
v1.2.0-beta.4
v1.2.0-beta.3
v1.2.0-beta.2
v1.2.0-beta.1
import { ScrollArea } from "@workspace/ui/components/scroll-area"

<ScrollArea className="h-72 w-48 rounded-md border">
  <div className="p-4">
    <h4 className="mb-4 text-sm leading-none font-medium">Tags</h4>
    {tags.map((tag) => (
      <React.Fragment key={tag}>
        <div className="text-sm">{tag}</div>
        <Separator className="my-2" />
      </React.Fragment>
    ))}
  </div>
</ScrollArea>
Horizontal scrolling
For a horizontal strip, let the content lay out at its natural width — flex w-max plus whitespace-nowrap on the area — and add <ScrollBar orientation="horizontal" /> as a child. The built-in vertical scrollbar stays out of the way since nothing overflows vertically, and because browsers only wheel-scroll the axis that overflows, ScrollArea translates a plain mouse wheel into horizontal scrolling here — trackpads swipe natively either way.
Valley
Dunes
Coast
Ridge
Forest
Mesa
Delta
Fjord
import { ScrollArea, ScrollBar } from "@workspace/ui/components/scroll-area"

<ScrollArea className="w-96 rounded-md border whitespace-nowrap">
  <div className="flex w-max gap-4 p-4">
    {artworks.map((artwork) => (
      <figure key={artwork} className="shrink-0">
        <div className="h-40 w-28 rounded-md bg-muted" />
        <figcaption className="pt-2 text-xs text-muted-foreground">
          {artwork}
        </figcaption>
      </figure>
    ))}
  </div>
  <ScrollBar orientation="horizontal" />
</ScrollArea>
Both directions
Content that overflows on both axes — wide tables, grids, code — gets the vertical scrollbar for free; add the horizontal ScrollBar and both render together, meeting in the corner that ScrollArea already includes.
1·1
1·2
1·3
1·4
1·5
1·6
1·7
1·8
1·9
1·10
1·11
1·12
2·1
2·2
2·3
2·4
2·5
2·6
2·7
2·8
2·9
2·10
2·11
2·12
3·1
3·2
3·3
3·4
3·5
3·6
3·7
3·8
3·9
3·10
3·11
3·12
4·1
4·2
4·3
4·4
4·5
4·6
4·7
4·8
4·9
4·10
4·11
4·12
5·1
5·2
5·3
5·4
5·5
5·6
5·7
5·8
5·9
5·10
5·11
5·12
6·1
6·2
6·3
6·4
6·5
6·6
6·7
6·8
6·9
6·10
6·11
6·12
7·1
7·2
7·3
7·4
7·5
7·6
7·7
7·8
7·9
7·10
7·11
7·12
8·1
8·2
8·3
8·4
8·5
8·6
8·7
8·8
8·9
8·10
8·11
8·12
9·1
9·2
9·3
9·4
9·5
9·6
9·7
9·8
9·9
9·10
9·11
9·12
10·1
10·2
10·3
10·4
10·5
10·6
10·7
10·8
10·9
10·10
10·11
10·12
11·1
11·2
11·3
11·4
11·5
11·6
11·7
11·8
11·9
11·10
11·11
11·12
12·1
12·2
12·3
12·4
12·5
12·6
12·7
12·8
12·9
12·10
12·11
12·12
13·1
13·2
13·3
13·4
13·5
13·6
13·7
13·8
13·9
13·10
13·11
13·12
14·1
14·2
14·3
14·4
14·5
14·6
14·7
14·8
14·9
14·10
14·11
14·12
15·1
15·2
15·3
15·4
15·5
15·6
15·7
15·8
15·9
15·10
15·11
15·12
16·1
16·2
16·3
16·4
16·5
16·6
16·7
16·8
16·9
16·10
16·11
16·12
17·1
17·2
17·3
17·4
17·5
17·6
17·7
17·8
17·9
17·10
17·11
17·12
18·1
18·2
18·3
18·4
18·5
18·6
18·7
18·8
18·9
18·10
18·11
18·12
19·1
19·2
19·3
19·4
19·5
19·6
19·7
19·8
19·9
19·10
19·11
19·12
20·1
20·2
20·3
20·4
20·5
20·6
20·7
20·8
20·9
20·10
20·11
20·12
<ScrollArea className="h-72 w-96 rounded-md border">
  <div className="w-max p-4">{/* wide and tall content */}</div>
  <ScrollBar orientation="horizontal" />
</ScrollArea>
Always visible
The type prop controls when the scrollbar shows: hover (default) reveals it while the pointer is over the area, scroll while scrolling, auto whenever content overflows, and always keeps it permanently visible. scrollHideDelay tunes how long hover and scroll wait before fading it out.

With type set to always, the scrollbar is mounted and visible from the start instead of appearing on hover.

Use it when the overflow itself is the signal — a long legal text, a changelog, a terms box — and hiding the scrollbar would hide the fact that there is more content below the fold.

The other values are hover (the default, shows while the pointer is over the area), scroll (shows while scrolling) and auto (behaves like a native scrollbar, visible whenever content overflows).

Whatever the type, scrolling itself stays native: wheel, trackpad, touch and keyboard all keep their platform behavior.

<ScrollArea className="h-48 w-96 rounded-md border" type="always">
  {/* long content */}
</ScrollArea>
Accessibility
Scroll Area styles the scrollbar without reimplementing scrolling — the viewport is a real scrollable element, so wheel, trackpad, touch and assistive technology behave exactly as they do with native overflow. The viewport is keyboard-focusable and shows the ring token as its focus indicator, so Tab plus arrow keys, PageUp and PageDown reach all content. The custom scrollbar itself is a pointer-only affordance; with the default hover type it is invisible until the pointer arrives, so prefer type always when seeing the overflow matters at a glance.
// keyboard users scroll the focused viewport natively —
// Tab into the area, then use arrows, PageUp/PageDown, Home/End
<ScrollArea className="h-72 rounded-md border" type="always">
  {/* content */}
</ScrollArea>
API Reference
Two parts. ScrollArea wraps the Radix Root and Viewport and always renders the vertical scrollbar and corner; every Radix Root prop passes through. ScrollBar wraps Scrollbar with the thumb inside and is added manually only for the horizontal orientation. Both scrollbars use logical properties, so RTL — via the dir prop or an ancestor — moves the vertical scrollbar to the correct side.
PartPropDefault
ScrollAreatype?: "hover" | "always" | "scroll" | "auto""hover"
ScrollAreascrollHideDelay?: number600
ScrollAreadir?: "ltr" | "rtl"inherited
ScrollBarorientation?: "vertical" | "horizontal""vertical"
ScrollBarforceMount?: true—

Tokens used

3 design tokens consumed by the Scroll Area component.

  • --border

    Border

    bg-border
  • --ring

    Ring

    focus-visible:ring-ring/50
  • --spacing

    Spacing unit (4px)

    w-2.5h-2.5