Carousel
an accessible carousel component that leverages native CSS Scroll Snap for smooth, performant scrolling between slides.
Features
Install
Install the component from your command line.
Anatomy
Import all parts and piece them together.
<script setup lang="ts">import * as carousel from '@destyler/carousel'import { normalizeProps, useMachine } from '@destyler/vue'import { computed, useId } from 'vue'
const [state, send] = useMachine( carousel.machine({ id: useId(), }))
const api = computed(() => carousel.connect(state.value, send, normalizeProps),)</script>
<template> <div v-bind="api.getRootProps()"> <div v-bind="api.getItemGroupProps()"> <div v-bind="api.getItemProps({ index:0 })"> <img> </div> </div>
<div v-bind="api.getControlProps()"> <button v-bind="api.getPrevTriggerProps()"/> <div v-bind="api.getIndicatorGroupProps()"> <button v-bind="api.getIndicatorProps({ index: 0 })" /> </div> <button v-bind="api.getNextTriggerProps()"/> </div> </div></template>import * as carousel from '@destyler/carousel'import { normalizeProps, useMachine } from '@destyler/react'import { useId } from 'react'
export default function Carousel() {
const [state, send] = useMachine( carousel.machine({ id: useId(), }) )
const api = carousel.connect(state, send, normalizeProps)
return ( <> <div {...api.getRootProps()}> <div {...api.getItemGroupProps()}> <div {...api.getItemProps({ index: 0 })}> <img /> </div> </div> <div {...api.getControlProps()}> <button {...api.getPrevTriggerProps()} /> <div {...api.getIndicatorGroupProps()}> <button {...api.getIndicatorProps({ index: 0 })}/> </div> <button {...api.getNextTriggerProps()}/> </div> </div> </> )}<script lang="ts"> import * as carousel from '@destyler/carousel' import { normalizeProps, useMachine } from '@destyler/svelte'
const uid = $props.id();
const [state, send] = useMachine( carousel.machine({ id: uid, }), )
const api = $derived(carousel.connect(state, send, normalizeProps))</script>
<div {...api.getRootProps()}> <div {...api.getItemGroupProps()}> <div {...api.getItemProps({ index: 0 })}> <img /> </div> </div>
<div {...api.getControlProps()}> <button {...api.getPrevTriggerProps()}> </button> <div {...api.getIndicatorGroupProps()}> <button {...api.getIndicatorProps({ index: 0 })}></button> </div> <button {...api.getNextTriggerProps()}></button> </div></div>import type { Component } from 'solid-js'import * as carousel from '@destyler/carousel'import { normalizeProps, useMachine } from '@destyler/solid'import { createMemo, createUniqueId } from 'solid-js'
const Carousel: Component = () => {
const [state, send] = useMachine( carousel.machine({ id: createUniqueId(), }))
const api = createMemo(() => carousel.connect(state, send, normalizeProps))
return ( <> <div {...api().getRootProps()}> <div {...api().getItemGroupProps()}> <div {...api().getItemProps({ index: 0 })}> <img /> </div> </div> <div {...api().getControlProps()}> <button {...api().getPrevTriggerProps()} /> <div {...api().getIndicatorGroupProps()}> <button {...api().getIndicatorProps({ index: 0 })}/> </div> <button {...api().getNextTriggerProps()} /> </div> </div> </> )}
export default CarouselVertical carousel
To create a vertical carousel, set the orientation property in the machine’s context to vertical.
const [state, send] = useMachine( carousel.machine({ orientation: "vertical", }),)Setting the initial slide
To set the initial slide of the carousel, pass the page property to the machine’s context.
The page corresponds to the scroll snap position index based on the layout.
It does not necessarily correspond to the index of the slide in the carousel.
const [state, send] = useMachine( carousel.machine({ page: 2, }),)Setting the number of slides to show at a time
To customize number of slides to show at a time,
set the slidesPerPage property in the machine’s context. The value must be an integer.
const [state, send] = useMachine( carousel.machine({ slidesPerPage: 2, }),)Setting the number of slides to move at a time
To customize number of slides to move at a time,
set the slidesPerMove property in the machine’s context.
The value must be an integer or auto.
If the value is auto,
the carousel will move the number of slides equal to the number of slides per page.
const [state, send] = useMachine( carousel.machine({ slidesPerMove: 2, }),)Ensure the slidesPerMove is less than or equal to the slidesPerPage to avoid skipping slides.
Setting the carousel should loop around
To allow looping of slides, set the loop property in the machine’s context to true.
const [state, send] = useMachine( carousel.machine({ loop: true, }),)Setting the gap between slides
To customize spacing between slides,
set the spacing property in the machine’s context to a valid CSS unit.
const [state, send] = useMachine( carousel.machine({ spacing: "20px", }),)Listening for page changes
When the carousel page changes, the onPageChange callback is invoked.
const [state, send] = useMachine( carousel.machine({ onPageChange(details) { // details => { page: number } console.log("selected page:", details.page) }, }),)Dragging the carousel
To allow dragging the carousel with the mouse,
set the allowMouseDrag property in the machine’s context to true.
const [state, send] = useMachine( carousel.machine({ allowMouseDrag: true, }),)Autoplaying the carousel
To allow the carousel to autoplay, set the autoplay property in the machine’s context to true.
const [state, send] = useMachine( carousel.machine({ autoplay: true, }),)Alternatively,
you can configure the autoplay interval by setting the delay property in the machine’s context.
const [state, send] = useMachine( carousel.machine({ autoplay: { delay: 2000 }, }),)Styling guide
Earlier, we mentioned that each carousel part has a data-part attribute added
to them to select and style them in the DOM.
[data-part="root"] { /* styles for the root part */}
[data-part="item-group"] { /* styles for the item-group part */}
[data-part="item"] { /* styles for the root part */}
[data-part="control"] { /* styles for the control part */}
[data-part="next-trigger"] { /* styles for the next-trigger part */}
[data-part="prev-trigger"] { /* styles for the prev-trigger part */}
[data-part="indicator-group"] { /* styles for the indicator-group part */}
[data-part="indicator"] { /* styles for the indicator part */}
[data-part="autoplay-trigger"] { /* styles for the autoplay-trigger part */}Active state
When a carousel’s indicator is active, a data-current attribute is set on the indicator.
[data-part="indicator"][data-current] { /* styles for the indicator's active state */}Methods and Properties
Machine Context
The carousel machine exposes the following context properties:
Partial<{ root: string; item: (index: number) => string; itemGroup: string; nextTrigger: string; prevTrigger: string; indicatorGroup: string; indicator: (index: number) => string; }>IntlTranslationsnumbernumber | "auto"boolean | { delay: number; }booleanbooleannumberstringstring(details: PageChangeDetails) => voidnumber | number[]"proximity" | "mandatory"number(details: DragStatusDetails) => void(details: AutoplayStatusDetails) => void"ltr" | "rtl"string() => ShadowRoot | Node | DocumentOrientationMachine API
The carousel api exposes the following methods:
numbernumber[]booleanbooleanbooleanboolean(index: number, instant?: boolean) => void(page: number, instant?: boolean) => void(instant?: boolean) => void(instant?: boolean) => void() => number() => void() => void(index: number) => boolean() => voidData Attributes
Root
data-scopedata-partdata-orientationItemGroup
data-scopedata-partdata-orientationdata-draggingItem
data-scopedata-partdata-indexdata-inviewdata-orientationControl
data-scopedata-partdata-orientationPrevTrigger
data-scopedata-partdata-orientationNextTrigger
data-scopedata-partdata-orientationIndicatorGroup
data-scopedata-partdata-orientationIndicator
data-scopedata-partdata-orientationdata-indexdata-readonlydata-currentAutoplayTrigger
data-scopedata-partdata-orientationdata-pressed