{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "expanding-cards",
  "type": "registry:ui",
  "title": "Expanding Cards",
  "description": "An Expanding Cards component for displaying a set of cards that expand on hover.",
  "files": [
    {
      "path": "src/registry/ui/expanding-cards.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { useId, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\n\nexport type ExpandingCardsItem = {\n  id: string;\n  title: string;\n  description?: React.ReactNode;\n  image: string;\n  imageAlt?: string;\n};\n\nexport type ExpandingCardsProps = Omit<\n  React.ComponentProps<\"ul\">,\n  \"defaultValue\" | \"onChange\"\n> & {\n  items: ExpandingCardsItem[];\n  /** Id of the expanded item. */\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n};\n\nexport function ExpandingCards({\n  items,\n  value,\n  defaultValue,\n  onValueChange,\n  className,\n  ...props\n}: ExpandingCardsProps) {\n  const baseId = useId();\n  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n  const activeId = value ?? uncontrolledValue ?? items[0]?.id;\n\n  const select = (next: string) => {\n    if (next === activeId) return;\n    if (value === undefined) setUncontrolledValue(next);\n    onValueChange?.(next);\n  };\n\n  return (\n    <ul\n      data-slot=\"expanding-cards\"\n      className={cn(\n        \"flex h-[32rem] w-full flex-col gap-2 md:h-96 md:flex-row md:gap-3\",\n        className\n      )}\n      {...props}\n    >\n      {items.map((item) => {\n        const isActive = item.id === activeId;\n        const contentId = `${baseId}-${item.id}`;\n\n        return (\n          <li\n            key={item.id}\n            data-active={isActive}\n            className={cn(\n              \"group relative min-h-0 min-w-0 grow basis-0 overflow-hidden rounded-xl bg-muted\",\n              \"transition-[flex-grow] duration-400 ease-[cubic-bezier(0.77,0,0.175,1)] motion-reduce:transition-none\",\n              \"data-[active=true]:grow-[5]\"\n            )}\n          >\n            {/* Plain <img> keeps the component portable outside Next.js. */}\n            {/* eslint-disable-next-line @next/next/no-img-element */}\n            <img\n              src={item.image}\n              alt={item.imageAlt ?? \"\"}\n              loading=\"lazy\"\n              decoding=\"async\"\n              className=\"absolute inset-0 size-full object-cover transition-transform duration-500 group-data-[active=true]:scale-105 motion-reduce:transition-none\"\n            />\n            <div\n              aria-hidden\n              className=\"absolute inset-0 bg-linear-to-t from-black/80 via-black/30 to-black/10\"\n            />\n\n            <button\n              type=\"button\"\n              aria-expanded={isActive}\n              aria-controls={item.description ? contentId : undefined}\n              onClick={() => select(item.id)}\n              className=\"absolute inset-0 cursor-pointer rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\"\n            >\n              <span className=\"sr-only\">{item.title}</span>\n            </button>\n\n            <div className=\"pointer-events-none absolute inset-x-0 bottom-0 flex flex-col gap-1 p-4 text-white md:p-5\">\n              <span\n                aria-hidden\n                className={cn(\n                  \"truncate text-base font-semibold\",\n                  isActive\n                    ? \"text-xl md:text-2xl\"\n                    : \"md:max-h-64 md:rotate-180 md:[writing-mode:vertical-rl]\"\n                )}\n              >\n                {item.title}\n              </span>\n              {item.description && (\n                <div\n                  id={contentId}\n                  inert={!isActive}\n                  className={cn(\n                    \"grid transition-[grid-template-rows,opacity] duration-400 ease-[cubic-bezier(0.77,0,0.175,1)] motion-reduce:transition-none\",\n                    isActive\n                      ? \"pointer-events-auto grid-rows-[1fr] opacity-100\"\n                      : \"grid-rows-[0fr] opacity-0\"\n                  )}\n                >\n                  <div className=\"overflow-hidden text-sm text-white/85\">\n                    {item.description}\n                  </div>\n                </div>\n              )}\n            </div>\n          </li>\n        );\n      })}\n    </ul>\n  );\n}\n"
    }
  ]
}
