Skip to content

ScrollArea ​

A container that wraps content and takes over the scrollbar appearance: a thin custom scrollbar that thickens on hover; type controls when the scrollbar is visible (auto / always / scroll / hover), and scroll events are throttled.

Basic usage ​

height fixes the viewport height and content that overflows scrolls vertically; the scrollbar renders as a thin bar that thickens on hover.

Fixed-height scrolling

Line 1: ScrollArea supports a custom scrollbar appearance

Line 2: thin bar that thickens on hover

Line 3: scroll events throttled into oas-scroll

Line 4: smooth wheel scrolling

Line 5: auto-hide supported

Line 6: horizontal content also gets the custom scrollbar

Line 7: colors use theme tokens

Line 8: great for lists, logs and long text

Line 9: no scrollbar when content fits

Line 10: custom content slot

Width and horizontal scrolling ​

width fixes the viewport width; wide content produces a horizontal scrollbar.

Horizontal scrolling

Card 1
Card 2
Card 3
Card 4
Card 5
Card 6

Visibility mode (type) ​

type controls when the scrollbar is visible, in four modes: always (default, visible whenever content overflows — see Basic usage), auto (shown while scrolling or hovering, fades out after stopping), scroll (shown only while scrolling, not on hover), hover (shown while hovering, hides immediately on leave). The legacy auto-hide boolean stays supported and equals type="auto"; when type is present it takes precedence, and invalid values fall back to always.

type=auto (equals legacy auto-hide)

Scroll to see the scrollbar: hidden normally, shown while scrolling

Stop for a second and the scrollbar fades out automatically

Hovering over the area also shows it temporarily

The legacy auto-hide is fully equivalent to type="auto"

Great for UIs where a scrollbar would distract from reading

type=scroll (shown only while scrolling)

The scrollbar appears while scrolling and fades out after stopping

Difference from auto: hovering alone does not show it

Great for reading UIs: position feedback while scrolling

No scrollbar distracting you while reading at rest

Recommended for long-form reading on mobile

type=hover (shown while hovering)

The scrollbar appears as soon as the pointer hovers the area

It hides immediately after the pointer leaves (with a fade)

Difference from auto: hovering shows it without any scrolling

Great when you want the scroll position visible at all times

Friendly to desktop mouse interaction

Scroll event ​

Scroll events are throttled with rAF and fire oas-scroll; detail carries { scrollTop, scrollLeft }.

oas-scroll event

Line 1: scroll events throttled

Line 2: detail carries scrollTop / scrollLeft

Line 3: great for scroll listeners and lazy loading

Line 4: complements virtual-list scrolling

Line 5: scrollbar position stays in sync

Line 6: keep scrolling to see the output

Line 7: throttling avoids high-frequency events

Line 8: the last example

scrollTop: 0

Programmatic scrolling ​

The component instance exposes scrollTo(options) / scrollToTop() / scrollToBottom() / scrollIntoView(selectorOrEl) methods (delegating to native viewport scrolling; only scrollToTop / scrollToBottom default to smooth scrolling and accept a behavior override, while scrollTo / scrollIntoView pass through native semantics) for host-controlled programmatic scrolling.

Programmatic scroll methods

Line 1: programmatic scrolling demo (buttons call instance methods)

Line 2: scrollTo accepts { top, left, behavior }

Line 3: scrollToTop / scrollToBottom in one call

Line 4: scrollIntoView locates an element inside the container

Target line: located by scrollIntoView

Line 6: smooth scrolling handled by the browser

Line 7: great for "back to top" and "load-more positioning"

Line 8: keep scrolling to the bottom

Line 9: more content to fill the height

Line 10: the last line

Scroll to bottomBack to topScroll to 120pxLocate target

Scroll shadow ​

scroll-shadow enables scroll edge shadows (CSS-only, same technique as the modal): the shadow fades at an edge as you scroll toward it, and both top and bottom edges show shadows while in the middle.

scroll-shadow

Line 1: scroll-shadow shows scroll edge shadows

Line 2: at the top, the upper shadow fades away

Line 3: in the middle, shadows appear at both edges

Line 4: hinting that more content exists in that direction

Line 5: at the bottom, the lower shadow fades away

Line 6: great for long lists, chat logs and news articles

Line 7: pure CSS, zero JS cost

Line 8: keep scrolling to watch the shadows change

Stick to bottom ​

With stick-to-bottom, when new content is appended and the user is already at the bottom (within 8px), the viewport auto-scrolls to the new bottom; appends never interrupt reading history. Perfect for chat and log streams.

stick-to-bottom

Message 1: welcome to stick-to-bottom
Message 2: appends auto-scroll to the bottom when you are there
Message 3: appends never interrupt reading history
Message 4: scroll to the bottom first, then hit append
Append a message

Click "Append a message" to see auto-sticking; scroll up to the middle first and append again — you won't be dragged back down.

End-reached event (oas-end-reached) ​

oas-end-reached fires when scrolling reaches the container bottom (or the horizontal right edge), with detail: { direction: 'bottom' | 'right' }. end-distance sets how many px before the edge counts as reached (default 0). After firing, you must leave the edge and come back to fire again — the classic infinite-scroll pattern.

oas-end-reached event

Initial 1: scroll to the bottom to fire oas-end-reached

Initial 2: end-distance=10 fires slightly early

Initial 3: new items are appended on fire

Initial 4: leave the bottom and come back to fire again

Initial 5: the classic infinite-scroll use case

Fired: 0

RTL ​

When the host sets dir="rtl", horizontal scrollbar behavior and wheel translation follow RTL semantics: in Chromium/Firefox the horizontal scrollLeft lives in the negative range [-max, 0], wheel-to-horizontal translation scrolls in the opposite direction from LTR, and the thumb position is computed from the absolute value.

RTL horizontal scrolling

Card 1 (starts right)
Card 2
Card 3
Card 4
Card 5
Card 6 (left end)

Under `dir="rtl"` the content starts right-aligned and the wheel/trackpad scroll direction is opposite to LTR. Note: the rendered horizontal scrollbar position follows the browser's native RTL behavior.

API ​

Methods ​

MethodDescription
scrollTo(options) / scrollTo(x, y)Scrolls to a position, options: { top?, left?, behavior?: 'auto' | 'smooth' } (delegates to the viewport)
scrollToTop(options?) / scrollToBottom(options?)Scrolls to the top/bottom, options: { behavior? }, smooth by default
scrollIntoView(selectorOrEl, options?)Scrolls an element inside the container into view; block / inline from options are passed through

oas-scroll-area ​

Attributes ​

AttributeDescriptionTypeDefault
auto-hideLegacy boolean: the scrollbar is shown only while scrolling/hovering, then auto-hides after a timeout — equivalent to type="auto" when type is absent; type takes precedence when presentboolean—
end-distanceTrigger distance for oas-end-reached (px, default 0): within N px of the bottom/right edge counts as reachedstring0
heightViewport height (px); when unset, grows with the content——
scroll-shadowScroll edge shadow (CSS-only): the shadow fades at each edge as you scroll, hinting that more content exists——
stick-to-bottomStick to bottom: when new content is appended and the user is at the bottom (≤8px), auto-scroll to the new bottom; never interrupts reading historyboolean—
typeScrollbar visibility mode (auto | always | scroll | hover, default always): auto shows while scrolling/hovering then auto-hides after a delay; always keeps it visible on overflow; scroll shows only while scrolling; hover shows only while hovering. Invalid values fall back to alwaysScrollAreaTypealways
widthViewport width (px); when unset, fills the host width——

Events ​

EventDescription
oas-end-reachedFired when scrolling reaches the container bottom (or the horizontal right edge), detail: { direction: 'bottom' | 'right' }; re-arms only after leaving the edge
oas-scrollScroll event (rAF-throttled), detail: { scrollTop, scrollLeft }

Slots ​

NameDescription
defaultScrollable content

Parts: ::part(viewport) is the scrolling viewport, ::part(track-v) / ::part(track-h) are the scroll tracks, ::part(thumb-v) / ::part(thumb-h) are the scroll thumbs. The viewport is focusable (tabindex="0") and scrolls with arrow keys.

Released under the MIT OR Apache-2.0 License.