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
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 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
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
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
| Method | Description |
|---|---|
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
| Attribute | Description | Type | Default |
|---|---|---|---|
auto-hide | Legacy 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 present | boolean | — |
end-distance | Trigger distance for oas-end-reached (px, default 0): within N px of the bottom/right edge counts as reached | string | 0 |
height | Viewport height (px); when unset, grows with the content | — | — |
scroll-shadow | Scroll edge shadow (CSS-only): the shadow fades at each edge as you scroll, hinting that more content exists | — | — |
stick-to-bottom | Stick to bottom: when new content is appended and the user is at the bottom (≤8px), auto-scroll to the new bottom; never interrupts reading history | boolean | — |
type | Scrollbar 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 always | ScrollAreaType | always |
width | Viewport width (px); when unset, fills the host width | — | — |
Events
| Event | Description |
|---|---|
oas-end-reached | Fired when scrolling reaches the container bottom (or the horizontal right edge), detail: { direction: 'bottom' | 'right' }; re-arms only after leaving the edge |
oas-scroll | Scroll event (rAF-throttled), detail: { scrollTop, scrollLeft } |
Slots
| Name | Description |
|---|---|
| default | Scrollable 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.