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.

Frodo — quarterly quest planning
Sam — office garden rota
Sméagol — lost property
Merry — second breakfast
Pippin — meeting room bookings
Aragorn — team check-in
Gimli — facilities requests
Legolas — desk plants

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

NameDescriptionPropertyTypeDefaultAllowed values
fillGrows into remaining space within a flex layout while keeping content independently scrollable.fillbooleanfalse—
orientationThe direction the content scrolls. Vertical scrolls up and down and clips sideways overflow; horizontal scrolls sideways and clips vertical overflow.orientationkeywordverticalvertical, horizontal
paddingThe 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.paddingkeywordnonenone, xs, sm, md, lg
sizeOptional 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.sizekeywordcontentcontent, sm, md, lg
trimRemoves the outer block margins of the first and last projected items when the surrounding panel owns its edge padding. Margins between items remain intact.trimbooleanfalse—

Framework props

NameTypeDefaultAllowed values
fillbooleanfalse—
orientation"vertical" | "horizontal"verticalvertical, horizontal
padding"none" | "xs" | "sm" | "md" | "lg"nonenone, xs, sm, md, lg
size"content" | "sm" | "md" | "lg"contentcontent, sm, md, lg
trimbooleanfalse—

Events

No events.

Slots

NamePurpose
defaultDefault 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

TokenFallback
--ui-scroll-fade-size24px
--ui-space-2—
--ui-space-3—
--ui-space-5—
--ui-space-6—