{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cards-carousel",
  "type": "registry:ui",
  "title": "Cards Carousel",
  "description": "Responsive image-card carousel with consumer-owned data and optional actions.",
  "dependencies": [
    "@mantine/carousel@^9",
    "@mantine/core@^9",
    "@mantine/hooks@^9"
  ],
  "docs": "Adapted from Mantine UI CardsCarousel at ffbf61c559f374a7ea28fcf00355e84dcbe9a908; hardcoded travel data became typed items with links or selection callbacks.",
  "css": {
    "@import \"@mantine/carousel/styles.css\"": {}
  },
  "registryDependencies": [
    "@house/mantine-ui-license"
  ],
  "files": [
    {
      "path": "registry/mantine-ui/cards-carousel/cards-carousel.tsx",
      "type": "registry:ui",
      "content": "/**\n * Adapted from Mantine UI's CardsCarousel at\n * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see\n * LICENSES/MANTINE-UI.txt after installation.\n */\n\nimport { Carousel, type CarouselProps } from \"@mantine/carousel\";\nimport {\n  type BoxProps,\n  Button,\n  type ElementProps,\n  type Factory,\n  factory,\n  type GetStylesApi,\n  Paper,\n  type StylesApiProps,\n  Text,\n  Title,\n  useMantineTheme,\n  useProps,\n  useStyles,\n} from \"@mantine/core\";\nimport { useMediaQuery } from \"@mantine/hooks\";\n\nimport classes from \"./cards-carousel.module.css\";\n\nexport type CardsCarouselStylesNames = \"root\" | \"card\" | \"title\" | \"category\";\n\nexport interface CardsCarouselItem {\n  id: string;\n  image: string;\n  title: string;\n  category: string;\n  href?: string;\n}\n\nexport interface CardsCarouselProps\n  extends BoxProps,\n    StylesApiProps<CardsCarouselFactory>,\n    ElementProps<\"div\", \"onSelect\"> {\n  items: readonly CardsCarouselItem[];\n  actionLabel?: string;\n  onSelect?: (item: CardsCarouselItem) => void;\n  /** Overrides the responsive default slide width (100% below `sm`, 50% above). */\n  slideSize?: CarouselProps[\"slideSize\"];\n  /** Overrides how many slides advance per navigation (default 1 below `sm`, 2 above). */\n  slidesToScroll?: number;\n}\n\nexport type CardsCarouselFactory = Factory<{\n  props: CardsCarouselProps;\n  ref: HTMLDivElement;\n  stylesNames: CardsCarouselStylesNames;\n}>;\n\ninterface CarouselItemCardProps {\n  item: CardsCarouselItem;\n  actionLabel: string;\n  onSelect?: (item: CardsCarouselItem) => void;\n  getStyles: GetStylesApi<CardsCarouselFactory>;\n  unstyled: boolean | undefined;\n}\n\nfunction CarouselItemCard({\n  item,\n  actionLabel,\n  onSelect,\n  getStyles,\n  unstyled,\n}: CarouselItemCardProps) {\n  const action = item.href ? (\n    <Button component=\"a\" href={item.href} variant=\"white\" color=\"dark\">\n      {actionLabel}\n    </Button>\n  ) : onSelect ? (\n    <Button variant=\"white\" color=\"dark\" onClick={() => onSelect(item)}>\n      {actionLabel}\n    </Button>\n  ) : null;\n\n  return (\n    <Paper\n      shadow=\"md\"\n      p=\"xl\"\n      radius=\"md\"\n      unstyled={unstyled}\n      {...getStyles(\"card\", {\n        style: {\n          backgroundImage: `linear-gradient(rgb(0 0 0 / 55%), rgb(0 0 0 / 55%)), url(${item.image})`,\n        },\n      })}\n    >\n      <div>\n        <Text {...getStyles(\"category\")} size=\"xs\">\n          {item.category}\n        </Text>\n        <Title order={3} {...getStyles(\"title\")}>\n          {item.title}\n        </Title>\n      </div>\n      {action}\n    </Paper>\n  );\n}\n\nexport const CardsCarousel = factory<CardsCarouselFactory>((_props) => {\n  const props = useProps(\"CardsCarousel\", null, _props);\n  const {\n    classNames,\n    className,\n    style,\n    styles,\n    unstyled,\n    vars,\n    attributes,\n    ref,\n    items,\n    actionLabel = \"View\",\n    onSelect,\n    slideSize,\n    slidesToScroll,\n    ...others\n  } = props;\n\n  const getStyles = useStyles<CardsCarouselFactory>({\n    name: \"CardsCarousel\",\n    classes,\n    props,\n    className,\n    style,\n    classNames,\n    styles,\n    unstyled,\n    attributes,\n    vars,\n  });\n\n  const theme = useMantineTheme();\n  const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`);\n\n  return (\n    <Carousel\n      ref={ref}\n      slideSize={slideSize ?? { base: \"100%\", sm: \"50%\" }}\n      slideGap={2}\n      emblaOptions={{ align: \"start\", slidesToScroll: slidesToScroll ?? (mobile ? 1 : 2) }}\n      nextControlProps={{ \"aria-label\": \"Next slide\" }}\n      previousControlProps={{ \"aria-label\": \"Previous slide\" }}\n      unstyled={unstyled}\n      {...getStyles(\"root\")}\n      {...others}\n    >\n      {items.map((item) => (\n        <Carousel.Slide key={item.id}>\n          <CarouselItemCard\n            item={item}\n            actionLabel={actionLabel}\n            onSelect={onSelect}\n            getStyles={getStyles}\n            unstyled={unstyled}\n          />\n        </Carousel.Slide>\n      ))}\n    </Carousel>\n  );\n});\n\nCardsCarousel.classes = classes;\nCardsCarousel.displayName = \"CardsCarousel\";\n\nexport namespace CardsCarousel {\n  export type Props = CardsCarouselProps;\n  export type StylesNames = CardsCarouselStylesNames;\n  export type Factory = CardsCarouselFactory;\n}\n"
    },
    {
      "path": "registry/mantine-ui/cards-carousel/cards-carousel.module.css",
      "type": "registry:file",
      "target": "@ui/cards-carousel.module.css",
      "content": "/*\n * Adapted from Mantine UI's CardsCarousel at\n * ffbf61c559f374a7ea28fcf00355e84dcbe9a908. MIT licensed; see\n * LICENSES/MANTINE-UI.txt after installation.\n */\n/* Structural selector only: the Carousel itself owns all default styling for\n * this element (see @mantine/carousel's own root class). This rule sets no\n * real CSS (an unused custom property) so the Styles API selector still has\n * a matching class in this module without introducing a visual default. */\n.root {\n  --cards-carousel-root: 1;\n}\n\n.card {\n  display: flex;\n  height: 440px;\n  flex-direction: column;\n  align-items: flex-start;\n  justify-content: space-between;\n  background-position: center;\n  background-size: cover;\n}\n\n.title {\n  margin-top: var(--mantine-spacing-xs);\n  color: var(--mantine-color-white);\n  font-family: var(--mantine-font-family-headings);\n  font-size: 32px;\n  font-weight: 600;\n  line-height: 1.2;\n}\n\n.category {\n  color: var(--mantine-color-white);\n  font-weight: 700;\n  text-transform: uppercase;\n}\n"
    }
  ],
  "meta": {
    "mantine": {
      "requires": ">=9",
      "provider": "MantineProvider",
      "stylesApi": {
        "CardsCarousel": [
          "root",
          "card",
          "title",
          "category"
        ]
      },
      "props": {
        "CardsCarousel": [
          {
            "name": "items",
            "type": "readonly CardsCarouselItem[]",
            "required": true,
            "description": "Provides the ordered set of cards rendered as carousel slides."
          },
          {
            "name": "actionLabel",
            "type": "string",
            "required": false,
            "default": "\"View\"",
            "description": "Sets the label shown on each card's action button."
          },
          {
            "name": "onSelect",
            "type": "(item: CardsCarouselItem) => void",
            "required": false,
            "description": "Called when a card's action button is clicked and the item has no href, rendering the button and handling its click."
          },
          {
            "name": "slideSize",
            "type": "CarouselProps[\"slideSize\"]",
            "required": false,
            "description": "Overrides the responsive default slide width (100% below the sm breakpoint, 50% above)."
          },
          {
            "name": "slidesToScroll",
            "type": "number",
            "required": false,
            "description": "Overrides how many slides advance per navigation (defaults to 1 below the sm breakpoint, 2 above)."
          },
          {
            "name": "...others",
            "type": "BoxProps & ElementProps<\"div\", \"onSelect\">",
            "description": "All other props are forwarded to the root <Carousel>, after its own slideGap, emblaOptions, and control props, so both can be overridden. Mantine Styles API props (classNames, styles, unstyled) are also accepted."
          }
        ]
      },
      "usage": {
        "path": "registry/mantine-ui/cards-carousel/cards-carousel.usage.tsx",
        "content": "import { CardsCarousel, type CardsCarouselItem } from \"@/components/ui/cards-carousel\";\n\nconst items: CardsCarouselItem[] = [\n  {\n    id: \"1\",\n    image:\n      \"https://images.unsplash.com/photo-1483728642387-6c3bdd6c93e5?auto=format&fit=crop&w=1080&q=80\",\n    title: \"Best forests to visit in North America\",\n    category: \"Nature\",\n    href: \"/articles/best-forests\",\n  },\n  {\n    id: \"2\",\n    image:\n      \"https://images.unsplash.com/photo-1508193638397-1c4234db14d8?auto=format&fit=crop&w=1080&q=80\",\n    title: \"Hiking gear you actually need\",\n    category: \"Outdoors\",\n  },\n  {\n    id: \"3\",\n    image:\n      \"https://images.unsplash.com/photo-1516214104703-d870798883c5?auto=format&fit=crop&w=1080&q=80\",\n    title: \"Planning a coastal road trip\",\n    category: \"Travel\",\n    href: \"/articles/coastal-road-trip\",\n  },\n];\n\nexport function FeaturedDestinations() {\n  return (\n    <CardsCarousel\n      items={items}\n      actionLabel=\"Read more\"\n      onSelect={(item) => console.log(\"selected\", item.id)}\n    />\n  );\n}\n"
      }
    }
  }
}
