{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "accordion",
  "title": "Accordion",
  "description": "A zero-dependency accessible disclosure list — single or multiple expansion, grid-rows animation, and full APG keyboard support.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "src/registry/ui/accordion.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronDown } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\nexport interface AccordionItem {\n  id: string\n  title: React.ReactNode\n  content: React.ReactNode\n}\n\nexport interface AccordionProps {\n  items: AccordionItem[]\n  /** \"single\" (default) closes the previously open item when a new one opens; \"multiple\" toggles items independently */\n  type?: \"single\" | \"multiple\"\n  /** item ids expanded on mount */\n  defaultOpen?: string[]\n  className?: string\n}\n\nexport function Accordion({ items, type = \"single\", defaultOpen, className }: AccordionProps) {\n  const uid = React.useId()\n  const [open, setOpen] = React.useState<string[]>(defaultOpen ?? [])\n  const triggerRefs = React.useRef<(HTMLButtonElement | null)[]>([])\n\n  const toggle = (id: string) => {\n    setOpen(prev => {\n      if (prev.includes(id)) return prev.filter(v => v !== id)\n      return type === \"single\" ? [id] : [...prev, id]\n    })\n  }\n\n  // APG accordion pattern: headers stay in the normal Tab sequence; arrow keys\n  // move focus between them and Home/End jump to the first/last header.\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>, index: number) => {\n    const triggers = triggerRefs.current\n    let next: number | null = null\n    if (e.key === \"ArrowDown\") next = (index + 1) % triggers.length\n    else if (e.key === \"ArrowUp\") next = (index - 1 + triggers.length) % triggers.length\n    else if (e.key === \"Home\") next = 0\n    else if (e.key === \"End\") next = triggers.length - 1\n    if (next !== null) {\n      e.preventDefault()\n      triggers[next]?.focus()\n    }\n  }\n\n  return (\n    <div className={cn(\"flex w-full flex-col\", className)}>\n      {items.map((item, index) => {\n        const isOpen = open.includes(item.id)\n        const triggerId = `${uid}-trigger-${item.id}`\n        const panelId = `${uid}-panel-${item.id}`\n        return (\n          <div className=\"border-b\" key={item.id}>\n            <h3 className=\"flex\">\n              <button\n                aria-controls={panelId}\n                aria-expanded={isOpen}\n                className={cn(\n                  \"flex w-full flex-1 cursor-pointer items-center justify-between gap-4 rounded-md py-4 text-left text-sm font-medium\",\n                  \"hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n                )}\n                id={triggerId}\n                onClick={() => toggle(item.id)}\n                onKeyDown={e => handleKeyDown(e, index)}\n                ref={el => {\n                  triggerRefs.current[index] = el\n                }}\n                type=\"button\"\n              >\n                {item.title}\n                <ChevronDown\n                  aria-hidden=\"true\"\n                  className={cn(\n                    \"size-4 shrink-0 text-muted-foreground transition-transform duration-200 motion-reduce:transition-none\",\n                    isOpen && \"rotate-180\",\n                  )}\n                />\n              </button>\n            </h3>\n            <div\n              aria-labelledby={triggerId}\n              className={cn(\n                \"grid transition-[grid-template-rows] duration-200 motion-reduce:transition-none\",\n                isOpen ? \"grid-rows-[1fr]\" : \"grid-rows-[0fr]\",\n              )}\n              id={panelId}\n              role=\"region\"\n            >\n              <div className=\"overflow-hidden\">\n                <div className=\"pb-4 text-sm text-muted-foreground\">{item.content}</div>\n              </div>\n            </div>\n          </div>\n        )\n      })}\n    </div>\n  )\n}\n\nexport default Accordion\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}