Scroll Area
A region that scrolls when its content outgrows it, and fades each edge that hides content so the reader can tell there is more. Use it for any scrolling region: a navigation list, a panel's body, a strip of controls.
Default
A vertical area scrolls its content. The bottom edge fades while more is below; scroll down and the top fades too, and each fade lifts once that edge has nothing past it.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-scroll-area/ui-scroll-area.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<ui-scroll-area
style="block-size: 10rem"
>
<ui-stack
gap="s"
>
<ui-card
padding="sm"
>Frodo — quarterly quest planning</ui-card>
<ui-card
padding="sm"
>Sam — office garden rota</ui-card>
<ui-card
padding="sm"
>Sméagol — lost property</ui-card>
<ui-card
padding="sm"
>Merry — second breakfast</ui-card>
<ui-card
padding="sm"
>Pippin — meeting room bookings</ui-card>
<ui-card
padding="sm"
>Aragorn — team check-in</ui-card>
<ui-card
padding="sm"
>Gimli — facilities requests</ui-card>
<ui-card
padding="sm"
>Legolas — desk plants</ui-card>
</ui-stack>
</ui-scroll-area><script setup>
import { UiCard, UiScrollArea, UiStack } from "@threadlabs/looma";
</script>
<template>
<UiScrollArea
style="block-size: 10rem"
>
<UiStack
gap="s"
>
<UiCard
padding="sm"
>Frodo — quarterly quest planning</UiCard>
<UiCard
padding="sm"
>Sam — office garden rota</UiCard>
<UiCard
padding="sm"
>Sméagol — lost property</UiCard>
<UiCard
padding="sm"
>Merry — second breakfast</UiCard>
<UiCard
padding="sm"
>Pippin — meeting room bookings</UiCard>
<UiCard
padding="sm"
>Aragorn — team check-in</UiCard>
<UiCard
padding="sm"
>Gimli — facilities requests</UiCard>
<UiCard
padding="sm"
>Legolas — desk plants</UiCard>
</UiStack>
</UiScrollArea>
</template>import { UiCard, UiScrollArea, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiScrollArea
style={{"blockSize":"10rem"}}
>
<UiStack
gap="s"
>
<UiCard
padding="sm"
>Frodo — quarterly quest planning</UiCard>
<UiCard
padding="sm"
>Sam — office garden rota</UiCard>
<UiCard
padding="sm"
>Sméagol — lost property</UiCard>
<UiCard
padding="sm"
>Merry — second breakfast</UiCard>
<UiCard
padding="sm"
>Pippin — meeting room bookings</UiCard>
<UiCard
padding="sm"
>Aragorn — team check-in</UiCard>
<UiCard
padding="sm"
>Gimli — facilities requests</UiCard>
<UiCard
padding="sm"
>Legolas — desk plants</UiCard>
</UiStack>
</UiScrollArea>;
}/** @jsxImportSource preact */
import { UiCard, UiScrollArea, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiScrollArea
style={{"blockSize":"10rem"}}
>
<UiStack
gap="s"
>
<UiCard
padding="sm"
>Frodo — quarterly quest planning</UiCard>
<UiCard
padding="sm"
>Sam — office garden rota</UiCard>
<UiCard
padding="sm"
>Sméagol — lost property</UiCard>
<UiCard
padding="sm"
>Merry — second breakfast</UiCard>
<UiCard
padding="sm"
>Pippin — meeting room bookings</UiCard>
<UiCard
padding="sm"
>Aragorn — team check-in</UiCard>
<UiCard
padding="sm"
>Gimli — facilities requests</UiCard>
<UiCard
padding="sm"
>Legolas — desk plants</UiCard>
</UiStack>
</UiScrollArea>;
}<script>
import { UiCard, UiScrollArea, UiStack } from "@threadlabs/looma";
</script>
<UiScrollArea
style="block-size: 10rem"
>
<UiStack
gap="s"
>
<UiCard
padding="sm"
>Frodo — quarterly quest planning</UiCard>
<UiCard
padding="sm"
>Sam — office garden rota</UiCard>
<UiCard
padding="sm"
>Sméagol — lost property</UiCard>
<UiCard
padding="sm"
>Merry — second breakfast</UiCard>
<UiCard
padding="sm"
>Pippin — meeting room bookings</UiCard>
<UiCard
padding="sm"
>Aragorn — team check-in</UiCard>
<UiCard
padding="sm"
>Gimli — facilities requests</UiCard>
<UiCard
padding="sm"
>Legolas — desk plants</UiCard>
</UiStack>
</UiScrollArea>import { Component } from "@angular/core";
import { UiCard, UiScrollArea, UiStack } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiScrollArea, UiStack, UiCard],
template: `
<div
ui-scroll-area
style="block-size: 10rem"
>
<div
ui-stack
[gap]="'s'"
>
<ng-template
ui-card
[padding]="'sm'"
>Frodo — quarterly quest planning</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Sam — office garden rota</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Sméagol — lost property</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Merry — second breakfast</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Pippin — meeting room bookings</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Aragorn — team check-in</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Gimli — facilities requests</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Legolas — desk plants</ng-template>
</div>
</div>
`,
})
export default class Example {}Horizontal
orientation="horizontal" scrolls sideways. Nothing inside can take focus, so the area is a labelled region with tabindex="0" that the arrow keys can scroll.
A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-scroll-area/ui-scroll-area.html"
>
<ui-scroll-area
orientation="horizontal"
role="region"
aria-label="Wide content"
tabindex="0"
>
<p
style="inline-size: 60rem"
>A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.</p>
</ui-scroll-area><script setup>
import { UiScrollArea } from "@threadlabs/looma";
</script>
<template>
<UiScrollArea
orientation="horizontal"
role="region"
aria-label="Wide content"
tabindex="0"
>
<p
style="inline-size: 60rem"
>A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.</p>
</UiScrollArea>
</template>import { UiScrollArea } from "@threadlabs/looma";
export default function Example() {
return <UiScrollArea
orientation="horizontal"
role="region"
aria-label="Wide content"
tabIndex="0"
>
<p
style={{"inlineSize":"60rem"}}
>A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.</p>
</UiScrollArea>;
}/** @jsxImportSource preact */
import { UiScrollArea } from "@threadlabs/looma";
export default function Example() {
return <UiScrollArea
orientation="horizontal"
role="region"
aria-label="Wide content"
tabIndex="0"
>
<p
style={{"inlineSize":"60rem"}}
>A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.</p>
</UiScrollArea>;
}<script>
import { UiScrollArea } from "@threadlabs/looma";
</script>
<UiScrollArea
orientation="horizontal"
role="region"
aria-label="Wide content"
tabindex="0"
>
<p
style="inline-size: 60rem"
>A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.</p>
</UiScrollArea>import { Component } from "@angular/core";
import { UiScrollArea } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiScrollArea],
template: `
<div
ui-scroll-area
[orientation]="'horizontal'"
role="region"
aria-label="Wide content"
tabindex="0"
>
<p
style="inline-size: 60rem"
>A single line far wider than the area, so the area scrolls sideways and fades its end edge until the line's end is in view.</p>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
fill | Grows into remaining space within a flex layout while keeping content independently scrollable. | fill | boolean | false | — |
orientation | The direction the content scrolls. Vertical scrolls up and down and clips sideways overflow; horizontal scrolls sideways and clips vertical overflow. | orientation | keyword | vertical | vertical, horizontal |
padding | The space inside the scrolling region, on Card's scale: xs is 0.5rem, sm 0.75rem, md 1.5rem, and lg 2rem. It scrolls with the content, so the scrollbar and fades stay at the edges. | padding | keyword | none | none, xs, sm, md, lg |
size | Optional height preset. content keeps intrinsic sizing; sm fits three compact 3.125rem result rows before the overflow fade, md is 20rem, and lg is 30rem. | size | keyword | content | content, sm, md, lg |
trim | Removes the outer block margins of the first and last projected items when the surrounding panel owns its edge padding. Margins between items remain intact. | trim | boolean | false | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
fill | boolean | false | — |
orientation | "vertical" | "horizontal" | vertical | vertical, horizontal |
padding | "none" | "xs" | "sm" | "md" | "lg" | none | none, xs, sm, md, lg |
size | "content" | "sm" | "md" | "lg" | content | content, sm, md, lg |
trim | boolean | false | — |
Events
No events.
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
No component tokens.
Shared tokens
| Token | Fallback |
|---|---|
--ui-scroll-fade-size | 24px |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-space-5 | — |
--ui-space-6 | — |