ScrollArea
A scrollable region with styled native scrollbars, consistent across platforms.
import { ... } from '@corndev/ui' - GET /pets/1
- GET /pets/2
- GET /pets/3
- GET /pets/4
- GET /pets/5
- GET /pets/6
- GET /pets/7
- GET /pets/8
- GET /pets/9
- GET /pets/10
- GET /pets/11
- GET /pets/12
- GET /pets/13
- GET /pets/14
- GET /pets/15
- GET /pets/16
- GET /pets/17
- GET /pets/18
- GET /pets/19
- GET /pets/20
- GET /pets/21
- GET /pets/22
- GET /pets/23
- GET /pets/24
- GET /pets/25
- GET /pets/26
- GET /pets/27
- GET /pets/28
- GET /pets/29
- GET /pets/30
HTML
No JavaScript required — these classes are the whole component.
<div class="corn-scroll-area" data-axis="y" style="max-block-size: 240px">Long content</div>
Classes
| Class |
|---|
.corn-scroll-area |
Attributes
| Attribute | Values |
|---|---|
data-axis | x|y|both |
data-hover-only | present or absent |
Vanilla JS
Optional. createScrollArea only adds and removes the classes and attributes above, so the HTML is equivalent. Reach for it when you need to change options at runtime.
import { createScrollArea } from '@corndev/ui';
import '@corndev/ui/theme';
const el = document.querySelector('#target');
const instance = createScrollArea(el, { /* options */ });
Options
| Option | Type | Default | Description |
|---|---|---|---|
axis | 'x'|'y'|'both' | 'y' | Which direction scrolls. The other axis is hidden rather than left to overflow. |
hoverOnly | boolean | false | Fade the scrollbar out until the pointer is over the region, as macOS does. The scrollbar stays reachable either way — this only affects the thumb’s colour, never whether it is there. |
maxHeight | number|string | undefined | Caps the region’s height, since a scroll area with no limit never scrolls. A number is treated as pixels. |
React
ScrollArea — from @corndev/ui-react.
maxHeight takes a number as pixels or any CSS length as-is, and merges with a style prop rather than replacing it.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
axis | 'x'|'y'|'both' | 'y' | Which direction scrolls. The other axis is hidden rather than left to overflow. |
hoverOnly | boolean | false | Fade the scrollbar out until the pointer is over the region, as macOS does. It stays reachable either way. |
maxHeight | number|string | undefined | Caps the height, since a region with no limit never scrolls. A number is treated as pixels. |
as | string|React.ComponentType | 'div' | Element or component to render as. |
className | string | undefined | Merged with the Corn UI classes. |
children | React.ReactNode | undefined |
overflow: auto looks different on macOS, Windows and Linux, and cannot be
styled uniformly on its own. This is that styling — applied to the browser’s
own scrollbar, not to a replacement drawn in JavaScript.
React usage
import { ScrollArea } from '@corndev/ui-react';
import '@corndev/ui/theme';
<ScrollArea maxHeight={240}>
{rows.map(row => <Row key={row.id} {...row} />)}
</ScrollArea>
Give it a height or it never scrolls. maxHeight takes a number as pixels
or any CSS length; a region with no limit grows instead of scrolling, which is
the most common reason this looks like it is doing nothing.
Why there is no custom scrollbar
A scrollbar drawn in JavaScript is where component libraries most often break things that were never theirs to break:
| Lost with a custom scrollbar | Kept here |
|---|---|
| Momentum scrolling on touch | native |
PageUp / PageDown, Home / End | native |
scrollIntoView, which focus management depends on | native |
| The OS setting for always-visible scrollbars | respected |
Nothing is rendered inside the region to track the scroll position, so there is nothing to fall out of sync, and no JavaScript to ship: the class alone does the work.
Axis
<ScrollArea axis="x">…</ScrollArea> {/* horizontal only */}
<ScrollArea axis="both">…</ScrollArea> {/* both */}
The axis you do not choose is hidden rather than left to overflow, so a wide child cannot quietly produce a second scrollbar.
Fading the scrollbar
hoverOnly shows the thumb only while the pointer is over the region, the way
macOS does:
<ScrollArea hoverOnly maxHeight="50vh">…</ScrollArea>
It changes the thumb’s colour, never whether the scrollbar exists — keyboard scrolling and the scrollbar’s width are unaffected.
Vanilla
<div class="corn-scroll-area" data-axis="y" style="max-block-size: 240px">…</div>
import { createScrollArea } from '@corndev/ui';
createScrollArea(document.querySelector('#log'), { axis: 'y', maxHeight: 240 });