{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-accordion",
  "title": "Image Accordion",
  "description": "Horizontal image panels that expand on hover to reveal their caption — an elegant, space-efficient gallery.",
  "registryDependencies": ["@godui/godui-theme"],
  "files": [
    {
      "path": "packages/components/src/image-accordion/image-accordion.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport type ImageAccordionPanel = {\n  /** Background image URL. */\n  image: string;\n  /** Panel title, shown in full when the panel is active. */\n  title: string;\n  /** Optional supporting line, revealed when the panel is active. */\n  description?: string;\n  /** Optional link — turns the panel into an anchor. */\n  href?: string;\n};\n\nexport type ImageAccordionProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> & {\n  /** Panels, left to right. Provide 3–6 for the best effect. */\n  panels: ImageAccordionPanel[];\n  /** Index of the panel open by default. */\n  defaultIndex?: number;\n  /** How much wider the active panel grows relative to the others. */\n  activeGrow?: number;\n  /** Height of the accordion (any CSS length). */\n  height?: string;\n};\n\nconst ImageAccordion = React.forwardRef<HTMLDivElement, ImageAccordionProps>(\n  (\n    {\n      panels,\n      defaultIndex = 0,\n      activeGrow = 5,\n      height = \"26rem\",\n      className,\n      style,\n      ...props\n    },\n    forwardedRef,\n  ) => {\n    const ref = React.useRef<HTMLDivElement>(null);\n    React.useImperativeHandle(\n      forwardedRef,\n      () => ref.current as HTMLDivElement,\n    );\n    const [active, setActive] = React.useState(defaultIndex);\n\n    return (\n      <div\n        ref={ref}\n        data-slot=\"image-accordion\"\n        className={`flex flex-col gap-2 overflow-hidden sm:flex-row ${className ?? \"\"}`}\n        style={{ height, ...style }}\n        onPointerLeave={() => setActive(defaultIndex)}\n        {...props}\n      >\n        {panels.map((panel, i) => {\n          const isActive = i === active;\n          const Tag = panel.href ? \"a\" : \"button\";\n          return (\n            <Tag\n              // biome-ignore lint/suspicious/noArrayIndexKey: panels are a fixed ordered set\n              key={i}\n              {...(panel.href\n                ? { href: panel.href }\n                : { type: \"button\" as const })}\n              data-active={isActive}\n              aria-expanded={isActive}\n              onPointerEnter={() => setActive(i)}\n              onClick={() => setActive(i)}\n              onFocus={() => setActive(i)}\n              className=\"group relative block min-h-[3rem] min-w-0 overflow-hidden rounded-2xl text-left [transition:flex-grow_550ms_cubic-bezier(0.22,1,0.36,1)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none sm:min-h-0 sm:min-w-[2rem]\"\n              style={{ flexGrow: isActive ? activeGrow : 1, flexBasis: 0 }}\n            >\n              {/* Image — desaturated when idle, full color + zoomed when active */}\n              <img\n                src={panel.image}\n                alt=\"\"\n                aria-hidden\n                className=\"absolute inset-0 size-full scale-105 object-cover brightness-90 saturate-[0.6] [transition:transform_700ms_ease,filter_550ms_ease] group-data-[active=true]:scale-100 group-data-[active=true]:brightness-100 group-data-[active=true]:saturate-100 motion-reduce:transition-none\"\n              />\n              {/* Legibility scrim */}\n              <div\n                aria-hidden\n                className=\"absolute inset-0 bg-gradient-to-t from-black/70 via-black/10 to-transparent\"\n              />\n\n              {/* Collapsed label — a vertical tab on idle panels */}\n              <span className=\"absolute bottom-4 left-1/2 -translate-x-1/2 text-sm font-medium whitespace-nowrap text-white opacity-80 [writing-mode:vertical-rl] rotate-180 [transition:opacity_300ms_ease] group-data-[active=true]:opacity-0 max-sm:top-1/2 max-sm:bottom-auto max-sm:left-5 max-sm:-translate-y-1/2 max-sm:translate-x-0 max-sm:rotate-0 max-sm:[writing-mode:horizontal-tb]\">\n                {panel.title}\n              </span>\n\n              {/* Expanded caption — slides up and fades in only when active */}\n              <div className=\"absolute inset-x-0 bottom-0 p-6 opacity-0 [transition:opacity_400ms_ease,transform_500ms_ease] [transform:translateY(12px)] group-data-[active=true]:translate-y-0 group-data-[active=true]:opacity-100 motion-reduce:transition-none\">\n                <span\n                  aria-hidden\n                  className=\"block h-px w-10 origin-left scale-x-0 bg-white/70 [transition:transform_500ms_ease_120ms] group-data-[active=true]:scale-x-100\"\n                />\n                <h3 className=\"mt-3 text-xl font-semibold whitespace-nowrap text-white\">\n                  {panel.title}\n                </h3>\n                {panel.description ? (\n                  <p className=\"mt-1 max-w-xs text-sm text-white/75\">\n                    {panel.description}\n                  </p>\n                ) : null}\n              </div>\n            </Tag>\n          );\n        })}\n      </div>\n    );\n  },\n);\nImageAccordion.displayName = \"ImageAccordion\";\n\nexport { ImageAccordion };\n",
      "type": "registry:ui",
      "target": "components/godui/image-accordion.tsx"
    }
  ],
  "type": "registry:ui"
}
