{
  "example": "import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, CarouselDots } from \"@godxjp/ui/lab\";\n\n// CarouselDots reads the Embla api from context — no setApi wiring needed.\n<Carousel opts={{ loop: true }}>\n  <CarouselContent>\n    <CarouselItem>1</CarouselItem>\n    <CarouselItem>2</CarouselItem>\n  </CarouselContent>\n  <CarouselPrevious />\n  <CarouselNext />\n  <CarouselDots />\n</Carousel>",
  "group": "data-display",
  "importPath": "@godxjp/ui/lab",
  "name": "Carousel",
  "props": [
    {
      "description": "Embla options.",
      "name": "opts",
      "type": "Parameters<typeof useEmblaCarousel>[0]"
    },
    {
      "description": "Embla plugins.",
      "name": "plugins",
      "type": "Parameters<typeof useEmblaCarousel>[1]"
    },
    {
      "description": "Receive the Embla api for custom logic (autoplay, external prev/next). NOT needed for dots — CarouselDots reads the api from context itself.",
      "name": "setApi",
      "type": "(api: CarouselApi) => void"
    }
  ],
  "rules": [
    3,
    6
  ],
  "storyPath": "data-display/carousel.tsx",
  "subParts": [
    "CarouselContent",
    "CarouselDots",
    "CarouselItem",
    "CarouselNext",
    "CarouselPrevious"
  ],
  "tagline": "Embla-backed carousel primitives: previous/next controls, CarouselDots indicators, and a context API.",
  "tier": "lab",
  "usage": [
    "DO compose the full set: `<Carousel>` › `<CarouselContent>` › many `<CarouselItem>`, with `<CarouselPrevious>`/`<CarouselNext>` for arrows and `<CarouselDots>` for the indicator row. Don't render items outside `<CarouselContent>` — the track is the scroll container.",
    "DO use `<CarouselDots>` for the active-slide indicator instead of wiring `setApi` by hand — it reads `selectedIndex`/`scrollSnaps` from the Carousel context, renders one `aria-current` dot per snap, and auto-hides when there is ≤1 slide.",
    "DON'T use a Carousel where ALL items must be seen/compared at once or be keyboard-reachable in reading order (e.g. a list of selectable options, a data table, primary navigation) — hiding content behind a swipe is an anti-pattern there; use a Grid/`ResponsiveGrid`, `ScrollArea`, or `Tabs`.",
    "DON'T autoplay without a pause-on-hover/focus control and reduced-motion respect — pass the Embla autoplay plugin via `plugins` only for non-essential decorative content, never for content the user must read.",
    "DO set `opts={{ loop: true }}` for galleries that wrap, and rely on the built-in disabling: `CarouselPrevious`/`CarouselNext` auto-disable at the ends (via `canScrollPrev`/`canScrollNext`) — don't hide them, let them grey out.",
    "DO give each `<CarouselItem>` real, meaningful content; the component already injects an 'N of M' slide label for screen readers, so don't add a redundant one (a consumer `aria-label` on the item overrides the default).",
    "DO size a MULTI-ITEM rail with `basis-*` on the item: `<CarouselItem className=\"basis-full sm:basis-1/2 lg:basis-1/3\">` is 1/2/3 per view, and `basis-4/5` leaves the \"peek\" that tells a phone user the rail scrolls. Use the FRACTIONAL ladder, never an arbitrary `basis-[82%]` (the audit rejects it). Fixed in gh#930 — before that the item carried `min-width: 100%`, which silently beat every `basis-*`: the documented 3-across composition computed `flex-basis: 33.3333%` and still painted one full-width slide 32px WIDER than its own rail. If a rail renders 1-up on an older version, that is the bug, not your className.",
    "DON'T wrap each `<CarouselItem>` in `<Reveal on=\"view\">`. The rail clips its slides, so the observer on a slide outside the window has an empty intersection rect and the slide never becomes visible — measured at 3 of 4 slides stuck at `opacity: 0`. Put ONE `<Reveal on=\"view\">` around the whole `<Carousel>`.",
    "DO expect the prev/next arrows to OVERLAY the rail edges — they are `position: absolute` inside the Carousel, so placing them in a caption row is not something a prop offers. Two knobs retune them: `--carousel-arrow-inset-block-start` for the vertical placement, and `--carousel-arrow-size` for the BUTTON box (default 2rem/32px, which clears WCAG 2.2 SC 2.5.8's 24px floor). A brand whose own standard is a 44px target sets `--carousel-arrow-size: 2.75rem` in its theme — the box and the glyph are separate decisions, so the mark stays on the icon scale via `--carousel-arrow-icon-size`. The box token is gh#931; before it, those were two bare literals and the target was the one dimension a theme could not reach."
  ],
  "useCases": [
    "Feature / onboarding highlight cards on a dashboard or landing surface, with CarouselDots showing position.",
    "Image or document thumbnail gallery (e.g. uploaded receipts / 物件写真) with looping and prev/next arrows.",
    "Horizontal stepping list of compact KPI or announcement cards that overflow the viewport width.",
    "Product/plan comparison cards on a marketing page where swiping between a few options is acceptable (not the primary action)."
  ]
}
