ScrollArea
A bounded, hand-drawn scroll viewport with a sketchy border and a token-themed scrollbar.
Overview
ScrollArea is a bounded scroll viewport: it draws a hand-drawn border around a scrollable region
whose native scrollbar is themed to match the design system. Use it to cap the height (or width) of
overflowing content — a long list, a code block, a panel of options — while keeping the sketchy
look consistent.
Anatomy
A sketchy border (@ghds/sketch-core) around an inner viewport. The viewport uses the standard
scrollbar-color / scrollbar-width CSS properties, so the thumb and track pick up
comp.scrollArea.* tokens without a custom scrollbar overlay (Firefox and Chromium 121+).
Constrain the region by passing maxHeight (or maxWidth) via style.
Variants & States
No interactive states — the only axis is orientation, which chooses the scroll axis.
| State | Status | Notes |
|---|---|---|
| Hover | Not implemented | — |
| Focus | Not implemented | The scroll region is keyboard-scrollable when focused, but paints no focus style itself. |
| Disabled | Not implemented | — |
| Loading | Not implemented | — |
| Error | Not implemented | — |
The orientation prop is 'vertical' (default), 'horizontal', or 'both'.
Usage
Do
- Constrain the box with maxHeight (or maxWidth) via style — that bound is what makes content scroll.
- Match orientation to the axis your content actually overflows on.
- Keep the scrolled content simple; the viewport is a plain overflow container.
Don't
- Don't nest scroll areas on the same axis; it traps the user in dueling scrollbars.
- Don't rely on the themed scrollbar for browsers older than Chromium 121 — they fall back to the native scrollbar.
Accessibility
- A region that scrolls should be reachable by keyboard. When the content isn’t naturally
focusable, give the viewport
tabIndex={0}and anaria-labelso keyboard users can scroll it. - The themed scrollbar uses standard
scrollbar-*properties, so it degrades gracefully to the native scrollbar — never hiding the scroll affordance entirely. - See the Accessibility Guide for shared conventions.
Content
N/A — ScrollArea has no fixed content model. It bounds whatever children you place inside and lets them overflow along the chosen axis.
Props API
React@ghds/react
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'vertical' | 'horizontal' | 'both' | 'vertical' | Which axis (or axes) the region scrolls. |
...rest | HTMLAttributes<HTMLDivElement> | — | Native <div> attributes pass through; use style to set maxHeight / maxWidth. |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'vertical' | 'horizontal' | 'both' | 'vertical' | Reflected attribute; which axis (or axes) the region scrolls. |
(default slot) | slot | — | The scrollable content. Set max-height (or max-width) on the host. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'vertical' | 'horizontal' | 'both' | 'vertical' | Which axis (or axes) the region scrolls (wraps native ScrollView). |
children | ReactNode | — | Scrollable content. |
style | StyleProp<ViewStyle> | — | Merged onto the bordered box; constrain height/width here (e.g. maxHeight). |
accessibilityLabel | string | — | Accessible label for the scroll region. |
testID | string | — | Test handle for queries. |
Code examples
// React — @ghds/react
import { ScrollArea } from '@ghds/react/scroll-area';
<ScrollArea orientation="vertical" style={{ maxHeight: 200, maxWidth: 360 }}>
{/* long content */}
</ScrollArea>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
import '@ghds/web-components/scroll-area';
</script>
<gh-scroll-area orientation="vertical" style="max-height: 200px; max-width: 360px;">
<!-- long content -->
</gh-scroll-area>
// React Native — @ghds/react-native
import { ScrollArea } from '@ghds/react-native/scroll-area';
<ScrollArea orientation="vertical" style={{ maxHeight: 200 }}>
{/* long content */}
</ScrollArea>;
Live Demo
React and Web Components render live below, driven by the same tokens — scroll either to reveal the
token-themed scrollbar, and toggle dark mode in the header to see both respond identically. React
Native can’t run in a browser without additional react-native-web wiring (not set up in this
site), so its usage is shown as a code sample instead. (On React Native the native scrollbar can’t
be themed, so the platform scroll indicators are kept as-is.)
React @ghds/react
Web Components @ghds/web-components
1. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
2. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
3. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
4. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
5. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
6. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
7. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.
8. The quick brown fox jumps over the lazy dog, again and again until the box overflows and the themed scrollbar appears.