Scroll
The Scroll creates a scrollable region with custom-styled scrollbars. Put your content inside; the scrollbar is added for you based on orientation.
The viewport defaults to a flex flex-col container. For horizontal scrolling, wrap your content in a single flex row (e.g. flex gap-2 w-max) or override the viewport class with viewportClassName / viewportClass — sibling block children inside the viewport will not scroll horizontally.
// apps/docs/src/components/examples/scroll/default/react.tsx
import { Scroll } from '@cloudvoyant/helical-react';
const tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`);
export default function ReactScrollDefault() {
return (
<Scroll className="h-72 w-64 rounded-md border border-border">
<div className="p-4">
<p className="mb-4 text-sm font-medium">Tags</p>
{tags.map((tag) => (
<div key={tag} className="border-t border-border py-2 text-sm text-muted-foreground">
{tag}
</div>
))}
</div>
</Scroll>
);
}<!-- apps/docs/src/components/examples/scroll/default/svelte.svelte -->
<script lang="ts">
import { Scroll } from '@cloudvoyant/helical-svelte';
const tags = Array.from({ length: 50 }, (_, i) => `v1.2.0-beta.${50 - i}`);
</script>
<Scroll class="h-72 w-64 rounded-md border border-border">
<div class="p-4">
<p class="mb-4 text-sm font-medium">Tags</p>
{#each tags as tag (tag)}
<div class="border-t border-border py-2 text-sm text-muted-foreground">{tag}</div>
{/each}
</div>
</Scroll>Examples
Both
// apps/docs/src/components/examples/scroll/both/react.tsx
import { Scroll } from '@cloudvoyant/helical-react';
export default function ReactScrollBoth() {
return (
<Scroll orientation="both" className="h-64 w-64 rounded-md border border-border">
<div className="h-[600px] w-[400px] p-4">
<p className="text-sm">Content that overflows both horizontally and vertically.</p>
</div>
</Scroll>
);
}<!-- apps/docs/src/components/examples/scroll/both/svelte.svelte -->
<script lang="ts">
import { Scroll } from '@cloudvoyant/helical-svelte';
</script>
<Scroll orientation="both" class="h-64 w-64 rounded-md border border-border">
<div class="h-[600px] w-[400px] p-4">
<p class="text-sm">Content that overflows both horizontally and vertically.</p>
</div>
</Scroll>Hidden
Horizontal
// apps/docs/src/components/examples/scroll/horizontal/react.tsx
import { Scroll } from '@cloudvoyant/helical-react';
export default function ReactScrollHorizontal() {
return (
<Scroll orientation="horizontal" className="h-24 w-full rounded-md border border-border">
<div className="flex gap-2 p-4">
{Array.from({ length: 20 }).map((_, i) => (
<div key={i} className="shrink-0 rounded-md bg-muted px-3 py-1 text-sm">
Item {i + 1}
</div>
))}
</div>
</Scroll>
);
}<!-- apps/docs/src/components/examples/scroll/horizontal/svelte.svelte -->
<script lang="ts">
import { Scroll } from '@cloudvoyant/helical-svelte';
const items = Array.from({ length: 20 }, (_, i) => i + 1);
</script>
<Scroll orientation="horizontal" class="h-24 w-full rounded-md border border-border">
<div class="flex gap-2 p-4">
{#each items as item (item)}
<div class="shrink-0 rounded-md bg-muted px-3 py-1 text-sm">Item {item}</div>
{/each}
</div>
</Scroll>Scroll Shadow
// apps/docs/src/components/examples/scroll/scroll-shadow/react.tsx
import { Scroll } from '@cloudvoyant/helical-react';
export default function ReactScrollScrollShadow() {
return (
<Scroll
className="h-64 w-64 rounded-md border border-border"
contentClassName="data-[overflow-y]:[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]"
>
<div className="p-4">
{Array.from({ length: 20 }).map((_, i) => (
<p key={i} className="border-t border-border py-2 text-sm text-muted-foreground">
Item {i + 1}
</p>
))}
</div>
</Scroll>
);
}<!-- apps/docs/src/components/examples/scroll/scroll-shadow/svelte.svelte -->
<script lang="ts">
import { Scroll } from '@cloudvoyant/helical-svelte';
const items = Array.from({ length: 20 }, (_, i) => i + 1);
</script>
<Scroll
class="h-64 w-64 rounded-md border border-border"
contentClass="data-[overflow-y]:[mask-image:linear-gradient(to_bottom,transparent,black_10%,black_90%,transparent)]"
>
<div class="p-4">
{#each items as i (i)}
<p class="border-t border-border py-2 text-sm text-muted-foreground">Item {i}</p>
{/each}
</div>
</Scroll>Sizes
// apps/docs/src/components/examples/scroll/sizes/react.tsx
import { Scroll } from '@cloudvoyant/helical-react';
const items = Array.from({ length: 12 }).map((_, i) => `Item ${i + 1}`);
export default function ReactScrollSizes() {
return (
<div className="flex gap-4">
<Scroll className="h-40 w-48 rounded-md border border-border [--scrollbar-size:0.25rem]">
<div className="p-4">
{items.map((item) => (
<div key={item} className="py-1 text-sm">
{item}
</div>
))}
</div>
</Scroll>
<Scroll className="h-40 w-48 rounded-md border border-border [--scrollbar-size:0.75rem]">
<div className="p-4">
{items.map((item) => (
<div key={item} className="py-1 text-sm">
{item}
</div>
))}
</div>
</Scroll>
</div>
);
}<!-- apps/docs/src/components/examples/scroll/sizes/svelte.svelte -->
<script lang="ts">
import { Scroll } from '@cloudvoyant/helical-svelte';
const items = Array.from({ length: 12 }, (_, i) => `Item ${i + 1}`);
</script>
<div class="flex gap-4">
<Scroll class="h-40 w-48 rounded-md border border-border [--scrollbar-size:0.25rem]">
<div class="p-4">
{#each items as item (item)}
<div class="py-1 text-sm">{item}</div>
{/each}
</div>
</Scroll>
<Scroll class="h-40 w-48 rounded-md border border-border [--scrollbar-size:0.75rem]">
<div class="p-4">
{#each items as item (item)}
<div class="py-1 text-sm">{item}</div>
{/each}
</div>
</Scroll>
</div>Thumb Styling
// apps/docs/src/components/examples/scroll/thumb-styling/react.tsx
import { Scroll } from '@cloudvoyant/helical-react';
export default function ReactScrollThumbStyling() {
return (
<Scroll className="h-64 w-64 rounded-md border border-border" thumbClassName="bg-primary">
<div className="p-4">
{Array.from({ length: 20 }).map((_, i) => (
<div key={i} className="py-1 text-sm">
Item {i + 1}
</div>
))}
</div>
</Scroll>
);
}<!-- apps/docs/src/components/examples/scroll/thumb-styling/svelte.svelte -->
<script lang="ts">
import { Scroll } from '@cloudvoyant/helical-svelte';
const items = Array.from({ length: 20 }, (_, i) => i + 1);
</script>
<Scroll class="h-64 w-64 rounded-md border border-border" thumbClass="bg-primary">
<div class="p-4">
{#each items as i (i)}
<div class="py-1 text-sm">Item {i}</div>
{/each}
</div>
</Scroll>API Reference
Scroll
Props
| Prop | Type | Default | Description |
|---|---|---|---|
orientation |
'vertical' | 'horizontal' | 'both' |
'vertical' |
Which scrollbar(s) to show. |
variant |
'default' | 'hidden' |
'default' |
hidden keeps content scrollable and renders no scrollbars. |
contentClassName / contentClass |
string |
— | Styles the content element (e.g. a mask for fading edges). |
viewportClassName / viewportClass |
string |
— | Overrides the viewport class (default flex h-full w-full flex-col). |
thumbClassName / thumbClass |
string |
— | Styles the scrollbar thumb. |
className / class |
string |
— | Set a fixed height here (e.g. h-48). |
Accessibility
Ark sets role="presentation" on the root and viewport and manages focus/keyboard scrolling; no hand-rolled roles or handlers are needed.