Carousel
A horizontally scrollable slide container with scroll-snap, optional arrow navigation, and a companion CarouselIndicator for dot-based navigation. Both components sync through a shared bind:currentIndex.
Import
import { Carousel, CarouselIndicator } from '@atom-forge/ui';
Components
Carousel — scrollable slide container
CarouselIndicator — dot indicator, synced via bind:currentIndex
Carousel Props
| Prop |
Type |
Default |
Description |
items |
T[] |
[] |
Array of slide data passed to the children snippet. |
currentIndex |
number (bindable) |
0 |
Active slide index. |
showArrows |
boolean |
true |
Show built-in prev/next arrow buttons. |
loop |
boolean |
false |
Wrap around from last slide to first. |
showcase |
number | [number, number] |
— |
Auto-advance delay in seconds. A tuple picks a random delay between the two values for each slide. |
children |
Snippet<[{ item: T; index: number }]> |
— |
(Required) Renders each slide. |
class |
string |
— |
Extra Tailwind classes on the root element. |
Carousel Exported Methods
| Method |
Description |
next() |
Advance to next slide (respects loop). |
prev() |
Go to previous slide (respects loop). |
goTo(index) |
Jump to a specific slide index. |
CarouselIndicator Props
| Prop |
Type |
Default |
Description |
total |
number |
0 |
Number of dots — should match items.length. |
currentIndex |
number (bindable) |
0 |
Active dot index. |
class |
string |
— |
Extra Tailwind classes on the container. |
Usage
<script lang="ts">
import { Carousel, CarouselIndicator } from '@atom-forge/ui';
const slides = [
{ id: 1, title: 'First' },
{ id: 2, title: 'Second' },
{ id: 3, title: 'Third' },
];
let currentIndex = $state(0);
let carousel: { prev(): void; next(): void; goTo(i: number): void };
</script>
<Carousel bind:this={carousel} items={slides} bind:currentIndex loop class="rounded-xl">
{#snippet children(slide)}
{@const s = slide.item}
<div class="h-48 flex items-center justify-center bg-canvas">
<p>{s.title}</p>
</div>
{/snippet}
</Carousel>
<CarouselIndicator total={slides.length} bind:currentIndex />
Showcase Mode
Use showcase to automatically advance slides. The value represents how long each slide remains visible:
<Carousel items={slides} showcase={4}>
{#snippet children(slide)}
{/snippet}
</Carousel>
<Carousel items={slides} showcase={[4, 6]}>
{#snippet children(slide)}
{/snippet}
</Carousel>
When the carousel reaches the last slide, showcase mode advances back to the first slide. The timer restarts after manual navigation, indicator changes, or user scrolling.
Behaviour
- Scroll → index: An
onscroll handler (debounced 100 ms) computes the current slide from scrollLeft and updates currentIndex.
- Index → scroll: A
$effect calls scrollTo whenever currentIndex changes externally (e.g. indicator click or goTo()). Manual scrolling is guarded by a userScrolling flag to prevent interference.
- Showcase: When
showcase is set, a timeout advances currentIndex after the configured delay. [min, max] picks a new random delay for each slide.
- Snap:
snap-x snap-mandatory with snap-center on each slide ensures pixel-perfect alignment after every scroll gesture.
When to use
Use for full-width slides navigated by scrolling, arrows, or an indicator.
Alternatives
Use Tabs for explicitly labeled content panels rather than sequential slides.
Setup and behavior
Bind the same $state index to Carousel and CarouselIndicator; there is no shared context provider. Keep total equal to items.length. External index assignment is not clamped; goTo() clamps only its own input. Avoid navigation methods on an empty item list. Showcase wraps regardless of loop and has no built-in pause-on-hover or pause control; add a user-controlled way to disable it when needed.
Carousel
A horizontally scrollable slide container with scroll-snap, optional arrow navigation, and a companion
CarouselIndicatorfor dot-based navigation. Both components sync through a sharedbind:currentIndex.Import
import { Carousel, CarouselIndicator } from '@atom-forge/ui';Components
Carousel— scrollable slide containerCarouselIndicator— dot indicator, synced viabind:currentIndexCarousel Props
itemsT[][]childrensnippet.currentIndexnumber(bindable)0showArrowsbooleantrueloopbooleanfalseshowcasenumber | [number, number]childrenSnippet<[{ item: T; index: number }]>classstringCarousel Exported Methods
next()loop).prev()loop).goTo(index)CarouselIndicator Props
totalnumber0items.length.currentIndexnumber(bindable)0classstringUsage
<script lang="ts"> import { Carousel, CarouselIndicator } from '@atom-forge/ui'; const slides = [ { id: 1, title: 'First' }, { id: 2, title: 'Second' }, { id: 3, title: 'Third' }, ]; let currentIndex = $state(0); let carousel: { prev(): void; next(): void; goTo(i: number): void }; </script> <Carousel bind:this={carousel} items={slides} bind:currentIndex loop class="rounded-xl"> {#snippet children(slide)} {@const s = slide.item} <div class="h-48 flex items-center justify-center bg-canvas"> <p>{s.title}</p> </div> {/snippet} </Carousel> <CarouselIndicator total={slides.length} bind:currentIndex />Showcase Mode
Use
showcaseto automatically advance slides. The value represents how long each slide remains visible:<Carousel items={slides} showcase={4}> {#snippet children(slide)} <!-- slide content --> {/snippet} </Carousel> <Carousel items={slides} showcase={[4, 6]}> {#snippet children(slide)} <!-- slide content --> {/snippet} </Carousel>When the carousel reaches the last slide, showcase mode advances back to the first slide. The timer restarts after manual navigation, indicator changes, or user scrolling.
Behaviour
onscrollhandler (debounced 100 ms) computes the current slide fromscrollLeftand updatescurrentIndex.$effectcallsscrollTowhenevercurrentIndexchanges externally (e.g. indicator click orgoTo()). Manual scrolling is guarded by auserScrollingflag to prevent interference.showcaseis set, a timeout advancescurrentIndexafter the configured delay.[min, max]picks a new random delay for each slide.snap-x snap-mandatorywithsnap-centeron each slide ensures pixel-perfect alignment after every scroll gesture.When to use
Use for full-width slides navigated by scrolling, arrows, or an indicator.
Alternatives
Use Tabs for explicitly labeled content panels rather than sequential slides.
Setup and behavior
Bind the same
$stateindex to Carousel and CarouselIndicator; there is no shared context provider. Keeptotalequal toitems.length. External index assignment is not clamped;goTo()clamps only its own input. Avoid navigation methods on an empty item list. Showcase wraps regardless ofloopand has no built-in pause-on-hover or pause control; add a user-controlled way to disable it when needed.