Cloud|

@k2b/ui

ScrollArea

A bounded scrollport that keeps content aligned as overflow appears and disappears.

ScrollArea@k2b/ui

Expand the activity list to reveal vertical fades. Scroll the document strip horizontally: each edge hint disappears when you reach the end.

Order #1048Dynamic activity region
Ready

The trailing edge is reserved before this content overflows. Watch the Ready badge while opening and closing the event list.

Show all processing events
  1. Validated inventory
  2. Reserved packing station
  3. Generated shipping label
  4. Assigned outbound lane
  5. Confirmed package weight
  6. Transferred to carrier
Purchase order

Recent document

Delivery note

Recent document

Service report

Recent document

Invoice

Recent document

Payment receipt

Recent document

TSX

Copy
<ScrollArea
role="region"
aria-label="Order activity"
scrollPreserveKey="order-activity"
style={{ height: "18rem" }}
>
<OrderSummary />
<details>
<summary>Show all processing events</summary>
<OrderEvents />
</details>
</ScrollArea>

ScrollArea creates one bounded scrollport whose content width stays stable when growing content starts or stops overflowing.

Top and bottom overflow hints are enabled by default. They use a subtle 16 px fade only where more content remains. Set scrollFade={false} when a specialized view needs an unmasked surface. Scrolling, resizing, and content changes update the hint; forced-color mode leaves content unmasked.

Components with browser code initialize themselves. Server-rendered scroll areas inside a workspace are also initialized by installAppWorkspaceController; Cloud already installs that controller. Arbitrary elements with overflow: auto are not modified.

Import

tsx
import { ScrollArea } from "@k2b/ui";

Use ScrollArea

Use ScrollArea when a region can cross its overflow boundary after loading more results, expanding a disclosure, or changing filters. It reserves a stable scrollbar gutter, so controls and text do not move horizontally when a classic scrollbar appears or disappears. Platforms with overlay scrollbars retain their native appearance and do not need reserved space.

The surrounding layout still owns the scroll area's height, flex growth, padding, and spacing. Give the component a bounded height or place it in a correctly sized grid or flex region. ScrollArea accepts normal div attributes and an optional class.

For compact asynchronous catalogs, use viewportSize="compact" instead of a custom height. It reserves 18.75rem (300 px at the default font size), capped at 60dvh, including any padding. Loading, empty, error, and populated states keep the same height; longer content scrolls within it. Sidebar previews expose the same setting as preview.viewportSize. Omit it for layout-owned sizing.

Pass scrollPreserveKey when enhanced navigation should restore this scrollport's position. Use a stable purpose-based key rather than an item id.

Keep one scroll owner for a full-height region. Do not wrap an existing scrolling component such as DetailPanel.Body in another ScrollArea. An editor can use ScrollArea as its outer scroll owner when the editor content grows naturally. Keep its toolbar outside the scroll area so controls remain fully visible. Notebooks uses this arrangement for its CodeMirror editor; its book view inherits the fade from AppWorkspace.Main.

The fades cover the edges of the scrollport. An editor that scrolls its own cursor into view must keep the cursor line out of them: Notebooks declares the fades as scroll-padding-block on the scroll area and passes the computed padding to CodeMirror's EditorView.scrollMargins. Do not add spacer lines or scroll manually.

DetailPanel also integrates its gutter with the surrounding workspace inset; that panel-specific geometry is not part of ScrollArea.

Horizontal strips

Use orientation="horizontal" for a bounded row of wider content. It uses left/right overflow hints on the same scrollport and supports right-to-left content. The default orientation="vertical" retains top/bottom hints. Hints disappear at the corresponding edge or when content fits. Resizing and content changes update both orientations. Keep fixed actions outside the port.

tsx
<ScrollArea orientation="horizontal" role="region" aria-label="Recent documents" tabIndex={0}>
  <div style={{ display: "flex", gap: "1rem", width: "max-content" }}>
    <DocumentCards />
  </div>
</ScrollArea>

Tabs already applies horizontal fades to its existing list; do not wrap it in another ScrollArea. PanelDialog.Body also owns its own vertical fades.

Accessibility

ScrollArea renders a normal div and does not add a landmark, accessible name, or tab stop. Keep a visible heading near the region. When a standalone scrollport needs to be announced as a region, pass role="region" together with aria-label or aria-labelledby. Add tabIndex={0} only when keyboard users otherwise have no focusable content through which to reach the scrollport.

Runtime

The scrollport is server-renderable and native scrolling works without client JavaScript. Overflow hints require browser code or the workspace controller. Native disclosures inside it continue to work before mount.

Example

tsx
<ScrollArea
  role="region"
  aria-label="Order activity"
  scrollPreserveKey="order-activity"
  style={{ height: "18rem" }}
>
  <OrderSummary />
  <details>
    <summary>Show all processing events</summary>
    <OrderEvents />
  </details>
</ScrollArea>

Connect a coding agent

The Cloud skill provides compact working instructions. MCP supplies exact current documentation when details matter.

CLI command
bunx skills add https://docs.example.com

This command installs the working instructions published by this website in the selected coding agent.

For exact, current details, also connect the MCP server through one of the agent tabs.

Installation uses the open-source Vercel Skills CLI.

The skill and MCP complement each other: the skill describes workflows, while MCP supplies current documentation.