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

AttributeValues
data-axisx|y|both
data-hover-onlypresent 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

OptionTypeDefaultDescription
axis'x'|'y'|'both''y'Which direction scrolls. The other axis is hidden rather than left to overflow.
hoverOnlybooleanfalseFade 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.
maxHeightnumber|stringundefinedCaps 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

PropTypeDefaultDescription
axis'x'|'y'|'both''y'Which direction scrolls. The other axis is hidden rather than left to overflow.
hoverOnlybooleanfalseFade the scrollbar out until the pointer is over the region, as macOS does. It stays reachable either way.
maxHeightnumber|stringundefinedCaps the height, since a region with no limit never scrolls. A number is treated as pixels.
asstring|React.ComponentType'div'Element or component to render as.
classNamestringundefinedMerged with the Corn UI classes.
childrenReact.ReactNodeundefined

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 scrollbarKept here
Momentum scrolling on touchnative
PageUp / PageDown, Home / Endnative
scrollIntoView, which focus management depends onnative
The OS setting for always-visible scrollbarsrespected

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 });