Morphos
Layout

Scroll Area

A custom scrollable container with styled scrollbars that integrates with the design system.

Interactive example

Installation

npm install @morphos/layout
pnpm add @morphos/layout
yarn add @morphos/layout
bun add @morphos/layout

Import

import { ScrollArea, ScrollAreaViewport, ScrollAreaScrollbar, ScrollAreaThumb } from '@morphos/layout'

Usage

@Component()
class MyComponent extends StatefulComponent {
  @State() scrollArea = new ScrollArea({ type: 'hover' })

  render() {
    return (
      <ScrollArea scrollArea={this.scrollArea}>
        <ScrollAreaViewport scrollArea={this.scrollArea}>
          <p>Long content that overflows the container...</p>
          {/* more content */}
        </ScrollAreaViewport>
        <ScrollAreaScrollbar scrollArea={this.scrollArea} orientation="vertical">
          <ScrollAreaThumb scrollArea={this.scrollArea} orientation="vertical" />
        </ScrollAreaScrollbar>
      </ScrollArea>
    )
  }
}

Scrolling happens natively on ScrollAreaViewport — the native scrollbar is hidden with CSS, and ScrollAreaScrollbar/ScrollAreaThumb render a decorative, ARIA-described overlay driven by the real scroll metrics.

Compound components

ComponentDescription
ScrollAreaRoot container — tracks scroll metrics and whether the content overflows
ScrollAreaViewportThe actual scrollable element (overflow: auto) — reports scroll metrics to ScrollArea
ScrollAreaScrollbarThe scrollbar track (role="scrollbar")
ScrollAreaThumbThe draggable-looking thumb, sized/positioned from real scroll metrics via CSS custom properties

Props — ScrollArea

PropTypeDefaultDescription
type"auto" | "always" | "scroll" | "hover" | "hidden""hover"When scrollbars are visible. This is exposed as data-type for CSS to act on — "hidden" is expected to hide the custom scrollbar entirely via CSS while the viewport still scrolls natively.
classstringCSS class
idstringHTML id

Methods — ScrollArea

MethodDescription
_onScroll(el: HTMLElement)Internal — called by ScrollAreaViewport on mount and on every scroll event to update tracked metrics

Props — ScrollAreaViewport

PropTypeDefaultDescription
scrollAreaScrollAreaThe ScrollArea state instance
childrenChildrenThe scrollable content
classstringCSS class
idstringHTML id

Props — ScrollAreaScrollbar

PropTypeDefaultDescription
scrollAreaScrollAreaThe ScrollArea state instance
orientation"vertical" | "horizontal""vertical"Scrollbar axis
childrenChildrenTypically a ScrollAreaThumb
classstringCSS class
idstringHTML id

Props — ScrollAreaThumb

PropTypeDefaultDescription
scrollAreaScrollAreaThe ScrollArea state instance — used to size/position the thumb proportionally to the visible fraction of content
orientation"vertical" | "horizontal""vertical"Which scroll axis to size/position against
classstringCSS class
idstringHTML id

data-* attributes

AttributeElementWhen present
data-typeScrollAreaAlways — reflects the type prop value
data-scrollableScrollAreaContent overflows the viewport on either axis
data-orientationScrollAreaScrollbarAlways — reflects the orientation prop

ScrollAreaThumb doesn't read data-* attributes for sizing — it sets the CSS custom properties --morphos-thumb-size and --morphos-thumb-offset directly on its own inline style, computed from the tracked scroll metrics.

Styling example

.morphos-scroll-area-viewport {
  scrollbar-width: none;
}

.morphos-scroll-area[data-type="hover"] .morphos-scroll-area-scrollbar {
  opacity: 0;
  transition: opacity var(--morphos-transition-medium);
}

.morphos-scroll-area[data-type="hover"]:hover .morphos-scroll-area-scrollbar {
  opacity: 1;
}

.morphos-scroll-area-scrollbar[data-orientation="vertical"] .morphos-scroll-area-thumb {
  top: var(--morphos-thumb-offset, 0%);
  height: var(--morphos-thumb-size, 100%);
}

On this page