Skip to content

Headless Core — Carousel

Powers Carousel internally. No autoplay timer, no swipe/drag handling, and no transform/scroll math — those are real DOM/timing concerns the consuming component owns. This factory only tracks which index is active and hands back the ARIA wiring.

import { createCarousel } from '@andersseen/headless-components/carousel';
const carousel = createCarousel({
slideCount: 5,
defaultIndex: 0,
onIndexChange: index => console.log('Active slide:', index),
});
carousel.actions.goToNext();
carousel.queries.isActive(1); // true after the call above
OptionTypeDefaultNotes
slideCountnumber0
defaultIndexnumber0Normalized modulo slideCount on creation.
autoplaybooleanfalseJust a state flag — starting/stopping a timer from it is up to you.
intervalnumber (ms)Accepted by the config type; not read internally — same reasoning as autoplay.
onIndexChange(index: number) => void

activeIndex, slideCount, autoplay.

MemberSignatureNotes
actions.goToNext() / goToPrev()() => voidWraps around at either end.
actions.goToSlide(index)(index: number) => voidAlso wraps — negative or out-of-range indexes are normalized modulo slideCount.
actions.setSlideCount(count)(count: number) => voidResets activeIndex to 0 if it’s now out of range.
actions.setAutoplay(v)(autoplay: boolean) => void
queries.isActive(index)(index: number) => boolean
GetterReturns
getTrackProps(trackId)aria-live ('off' while autoplay, else 'polite'), aria-label: 'Slides'
getSlideProps(index, label?)role: 'group', aria-roledescription: 'slide', aria-label (defaults to Slide N), aria-hidden (true for every non-active slide)
getPrevButtonProps(trackId) / getNextButtonProps(trackId)aria-controls: trackId, aria-label

aria-live switching off during autoplay is deliberate — a screen reader shouldn’t announce every auto-advance, only manual navigation.

Prev Next

Primitives — every factory above follows the same state/subscribe/actions/queries/prop-getter shape this one does.