Experience System
Components

Carousel

A carousel with motion and swipe for slides, built on Embla Carousel.

1
2
3
4
5

Installation

The component is exported from @by/experience-system. Add the package with your package manager:

pnpm add @by/experience-system

In this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.

Composition

Use the following composition to build a Carousel:

Carousel
├── CarouselContent
│   └── CarouselItem (repeat per slide)
├── CarouselPrevious
└── CarouselNext

CarouselContent owns the Embla viewport ref; each slide is a CarouselItem. Place CarouselPrevious and CarouselNext as siblings of CarouselContent (not inside the scrollable track). ThemeProvider passes Embla direction for RTL-aware arrows and keyboard navigation.

Usage

import {
  Carousel,
  CarouselContent,
  CarouselItem,
  CarouselNext,
  CarouselPrevious,
} from '@by/experience-system';

The carousel primitives are client components ('use client'). Use them inside client boundaries or other client components.

<Carousel className="w-full max-w-xs">
  <CarouselContent>
    {Array.from({ length: 3 }).map((_, index) => (
      <CarouselItem key={index}>
        <div className="flex items-center justify-center p-6 text-2xl font-semibold">{index + 1}</div>
      </CarouselItem>
    ))}
  </CarouselContent>
  <CarouselPrevious />
  <CarouselNext />
</Carousel>

Examples

The live preview at the top of this page uses carousel-usage (slides in Card shells with prev/next).

Sizes

Set slide width with basis-* on CarouselItem (for example basis-1/3 for three visible slides at a breakpoint).

1
2
3
4
5

Spacing

Use negative margin on CarouselContent and matching padding on each CarouselItem for gap between slides (RTL-friendly, for example -ms-*! / ps-*! horizontally).

1
2
3
4
5

Vertical

Set orientation="vertical" on Carousel. For spacing, use -mt-*! on content and pt-*! on items.

1
2
3
4
5

With API

Pass setApi to receive the Embla API when ready—use it to read the selected index, subscribe to select, or call scroll methods.

1
2
3
4
5
Slide 0 of 0

API Reference

Subsection titles name the exports from @by/experience-system. Embla behavior follows the Embla Carousel API.

Root region wrapping the carousel context.

PropTypeDefault
orientationhorizontal | verticalhorizontal
optsEmbla options
pluginsEmbla plugins
setApi(api: CarouselApi) => void
classNamestring

Also accepts standard div attributes. Renders role="region" and aria-roledescription="carousel".

Data attributeValues
data-slotcarousel

CarouselContent

Scrollable track (viewport ref for Embla).

PropTypeDefault
classNamestring

Also accepts standard div attributes.

Data attributeValues
data-slotcarousel-viewport on the overflow wrapper; carousel-content on the inner flex track

CarouselItem

One slide.

PropTypeDefault
classNamestring

Also accepts standard div attributes. Renders role="group" and aria-roledescription="slide".

Data attributeValues
data-slotcarousel-item

CarouselPrevious

Previous control; disabled at the start unless loop is set in opts. Extends Button props (variant defaults to ghost, size to icon-sm).

PropTypeDefault
variantButton variantghost
sizeButton sizeicon-sm
classNamestring

Also accepts other Button / button props. Default aria-label is Previous slide (supplement with visible labels when needed).

Data attributeValues
data-slotcarousel-previous

CarouselNext

Next control; disabled at the end unless loop is set in opts. Same Button integration as CarouselPrevious.

PropTypeDefault
variantButton variantghost
sizeButton sizeicon-sm
classNamestring

Also accepts other Button / button props. Default aria-label is Next slide.

Data attributeValues
data-slotcarousel-next

useCarousel and CarouselApi

useCarousel reads carousel context inside subcomponents. CarouselApi is the Embla instance type exported for setApi and subscriptions.

Accessibility

  • Prefer visible labels or aria-label on CarouselPrevious / CarouselNext when the carousel is not described elsewhere.
  • ThemeProvider supplies Embla direction for RTL; keep slide markup perceivable for keyboard and screen reader users.

Source in the repo: packages/experience-system/src/components/Carousel/Carousel.tsx and useCarousel.ts.