{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "accordion",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@radix-ui/react-accordion", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "accordion.tsx",
      "content": "\"use client\";\n\nimport * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport { AnimatePresence, motion, type Transition } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst componentThemeClassName =\n  \"[--ic-background:#ffffff] [--ic-foreground:#111111] [--ic-primary:#111111] [--ic-secondary:#646b75] [--ic-surface-border:#e9edf2] [--ic-border:#e3e7ec] [--ic-card:#ffffff] [--ic-card-foreground:#111111] [--ic-muted:#f5f7fa] [--ic-muted-foreground:#6d7480] [--ic-accent:#f3f5f8] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--ic-accent-foreground:#111111] [--ic-input:#e3e7ec] [--ic-ring:rgba(17,17,17,0.16)] [--ic-destructive:#dc2626] [--ic-paper:#fcfcfd] [--ic-popover-foreground:#111111] [--ic-brand:#0ea5e9] [--ic-brand-soft:#bae6fd] [--ic-shadow-soft:0_18px_38px_-24px_rgba(15,23,42,0.35)] [--ic-chart-1:oklch(0.52_0.19_254)] [--ic-chart-2:oklch(0.74_0.11_232)] [--ic-chart-3:oklch(0.42_0.16_262)] [--ic-chart-4:oklch(0.84_0.07_228)] [--ic-chart-5:oklch(0.62_0.14_240)] [--color-background:var(--ic-background)] [--color-foreground:var(--ic-foreground)] [--color-primary:var(--ic-primary)] [--color-secondary:var(--ic-secondary)] [--color-border:var(--ic-border)] [--color-card:var(--ic-card)] [--color-card-foreground:var(--ic-card-foreground)] [--color-muted:var(--ic-muted)] [--color-muted-foreground:var(--ic-muted-foreground)] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--color-input:var(--ic-input)] [--color-ring:var(--ic-ring)] [--color-destructive:var(--ic-destructive)] [--color-paper:var(--ic-paper)] [--color-popover-foreground:var(--ic-popover-foreground)] [--color-brand:var(--ic-brand)] [--color-brand-soft:var(--ic-brand-soft)] [--color-chart-1:var(--ic-chart-1)] [--color-chart-2:var(--ic-chart-2)] [--color-chart-3:var(--ic-chart-3)] [--color-chart-4:var(--ic-chart-4)] [--color-chart-5:var(--ic-chart-5)] dark:[--ic-background:#111111] dark:[--ic-foreground:#f6f3ec] dark:[--ic-primary:#f6f3ec] dark:[--ic-secondary:#cbc6bb] dark:[--ic-surface-border:#2a2a25] dark:[--ic-border:#2b2a25] dark:[--ic-card:#111111] dark:[--ic-card-foreground:#f6f3ec] dark:[--ic-muted:#171716] dark:[--ic-muted-foreground:#9a958a] dark:[--ic-accent:#1a1a18] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] dark:[--ic-accent-foreground:#f6f3ec] dark:[--ic-input:#2b2a25] dark:[--ic-ring:rgba(246,243,236,0.18)] dark:[--ic-destructive:#f87171] dark:[--ic-paper:#171716] dark:[--ic-popover-foreground:#f6f3ec] dark:[--ic-brand:#38bdf8] dark:[--ic-brand-soft:#0c4a6e] dark:[--ic-shadow-soft:0_20px_44px_-28px_rgba(0,0,0,0.6)] dark:[--ic-chart-1:oklch(0.68_0.17_250)] dark:[--ic-chart-2:oklch(0.82_0.09_225)] dark:[--ic-chart-3:oklch(0.58_0.15_260)] dark:[--ic-chart-4:oklch(0.75_0.12_235)] dark:[--ic-chart-5:oklch(0.88_0.06_220)]\";\n\nexport interface AccordionItem {\n  id: string;\n  title: string;\n  content: React.ReactNode;\n}\n\nexport type AccordionVariant = \"default\" | \"quiet\";\n\nexport interface AccordionProps {\n  items: AccordionItem[];\n  className?: string;\n  multiple?: boolean;\n  variant?: AccordionVariant;\n}\n\nconst contentShellTransition: Transition = {\n  height: {\n    type: \"spring\",\n    stiffness: 138,\n    damping: 27,\n    mass: 0.98,\n  },\n  opacity: { duration: 0.26, ease: [0.18, 1, 0.32, 1] },\n};\n\nconst contentMaskTransition: Transition = {\n  duration: 0.38,\n  ease: [0.16, 1, 0.3, 1],\n};\n\nconst contentCopyTransition: Transition = {\n  y: {\n    type: \"spring\",\n    stiffness: 146,\n    damping: 23,\n    mass: 0.98,\n  },\n  scale: {\n    duration: 0.32,\n    ease: [0.18, 1, 0.32, 1],\n  },\n  opacity: {\n    duration: 0.22,\n    ease: [0.18, 1, 0.32, 1],\n    delay: 0.05,\n  },\n  filter: {\n    duration: 0.28,\n    ease: [0.18, 1, 0.32, 1],\n    delay: 0.05,\n  },\n};\n\ntype AccordionRowProps = {\n  item: AccordionItem;\n  index: number;\n  isOpen: boolean;\n};\n\nfunction getRowClassName(index: number) {\n  return cn(\"group\", index !== 0 && \"border-border/80 border-t\");\n}\n\nfunction getTriggerClassName(isOpen: boolean) {\n  return isOpen ? \"px-1 pt-5 pb-3\" : \"px-1 py-5\";\n}\n\nfunction getContentWrapClassName() {\n  return \"px-1 pr-12 pb-5\";\n}\n\nfunction getContentMaskClassName() {\n  return \"overflow-hidden\";\n}\n\nfunction getContentCopyClassName() {\n  return \"space-y-3 text-sm leading-relaxed text-muted-foreground will-change-transform [&_a]:font-medium [&_a]:underline [&_a]:underline-offset-4 [&_li+li]:mt-1.5 [&_ol]:mt-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p+p]:mt-3 [&_ul]:mt-3 [&_ul]:list-disc [&_ul]:pl-5\";\n}\n\nfunction getQuietContentClassName() {\n  return \"max-w-2xl pl-7 text-sm leading-relaxed text-muted-foreground [&_a]:font-medium [&_a]:underline [&_a]:underline-offset-4 [&_li+li]:mt-1.5 [&_ol]:mt-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p+p]:mt-3 [&_ul]:mt-3 [&_ul]:list-disc [&_ul]:pl-5\";\n}\n\nfunction getQuietContentWrapClassName() {\n  return \"pt-1.5\";\n}\n\nfunction getQuietContentMaskClassName() {\n  return \"overflow-hidden\";\n}\n\nfunction AccordionContent({\n  contentCopy,\n  isOpen,\n  maskClassName,\n  wrapClassName,\n}: {\n  contentCopy: React.ReactNode;\n  isOpen: boolean;\n  maskClassName: string;\n  wrapClassName: string;\n}) {\n  return (\n    <AnimatePresence initial={false}>\n      {isOpen ? (\n        <AccordionPrimitive.Content asChild forceMount>\n          <motion.div\n            animate={{\n              height: \"auto\",\n              opacity: 1,\n              clipPath: \"inset(0% 0% 0% 0%)\",\n            }}\n            className=\"overflow-hidden\"\n            exit={{\n              height: 0,\n              opacity: 0,\n              clipPath: \"inset(0% 0% 100% 0%)\",\n            }}\n            initial={{\n              height: 0,\n              opacity: 0,\n              clipPath: \"inset(0% 0% 100% 0%)\",\n            }}\n            transition={contentShellTransition}\n          >\n            <div className={wrapClassName}>\n              <motion.div\n                animate={{\n                  clipPath: \"inset(0% 0% 0% 0%)\",\n                  opacity: 1,\n                }}\n                className={maskClassName}\n                exit={{\n                  clipPath: \"inset(0% 100% 0% 0%)\",\n                  opacity: 0.68,\n                }}\n                initial={{\n                  clipPath: \"inset(0% 100% 0% 0%)\",\n                  opacity: 0.68,\n                }}\n                transition={contentMaskTransition}\n              >\n                {contentCopy}\n              </motion.div>\n            </div>\n          </motion.div>\n        </AccordionPrimitive.Content>\n      ) : null}\n    </AnimatePresence>\n  );\n}\n\nfunction AccordionTriggerLabel({ title }: { title: string }) {\n  return (\n    <motion.span\n      animate={{ x: 0 }}\n      className=\"pr-4 font-medium text-[15px] text-foreground leading-6 tracking-[-0.02em] sm:text-base\"\n      transition={{ type: \"spring\", stiffness: 300, damping: 28 }}\n    >\n      {title}\n    </motion.span>\n  );\n}\n\nfunction AccordionTriggerIndicator({ isOpen }: { isOpen: boolean }) {\n  return (\n    <motion.div\n      animate={{ opacity: isOpen ? 1 : 0.72 }}\n      aria-hidden\n      className=\"mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center text-foreground\"\n      transition={{ type: \"spring\", stiffness: 360, damping: 24 }}\n    >\n      <span className=\"relative flex h-3 w-3 items-center justify-center\">\n        <span className=\"absolute h-px w-3 rounded-full bg-current\" />\n        <motion.span\n          animate={{\n            opacity: isOpen ? 0 : 1,\n            scaleY: isOpen ? 0 : 1,\n          }}\n          className=\"absolute h-3 w-px origin-center rounded-full bg-current\"\n          transition={{\n            duration: 0.18,\n            ease: [0.22, 1, 0.36, 1],\n          }}\n        />\n      </span>\n    </motion.div>\n  );\n}\n\nfunction AccordionRow({ item, index, isOpen }: AccordionRowProps) {\n  const contentCopy = (\n    <motion.div\n      animate={{\n        opacity: 1,\n        y: 0,\n        scale: 1,\n        filter: \"blur(0px)\",\n      }}\n      className={getContentCopyClassName()}\n      exit={{\n        opacity: 0,\n        y: -2,\n        scale: 0.996,\n        filter: \"blur(1.5px)\",\n      }}\n      initial={{\n        opacity: 0,\n        y: 7,\n        scale: 0.998,\n        filter: \"blur(3px)\",\n      }}\n      transition={contentCopyTransition}\n    >\n      {item.content}\n    </motion.div>\n  );\n\n  return (\n    <AccordionPrimitive.Item className={getRowClassName(index)} value={item.id}>\n      <motion.div\n        animate={{ opacity: 1, y: 0 }}\n        initial={{ opacity: 0, y: 12 }}\n        transition={{\n          delay: index * 0.05,\n          duration: 0.3,\n          ease: [0.22, 1, 0.36, 1],\n        }}\n      >\n        <AccordionPrimitive.Header className=\"flex\">\n          <AccordionPrimitive.Trigger\n            className={cn(\n              \"flex w-full cursor-pointer items-start justify-between gap-6 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n              getTriggerClassName(isOpen)\n            )}\n          >\n            <AccordionTriggerLabel title={item.title} />\n            <AccordionTriggerIndicator isOpen={isOpen} />\n          </AccordionPrimitive.Trigger>\n        </AccordionPrimitive.Header>\n\n        <AccordionContent\n          contentCopy={contentCopy}\n          isOpen={isOpen}\n          maskClassName={getContentMaskClassName()}\n          wrapClassName={getContentWrapClassName()}\n        />\n      </motion.div>\n    </AccordionPrimitive.Item>\n  );\n}\n\nfunction AccordionQuietRow({\n  index,\n  item,\n  isOpen,\n}: {\n  index: number;\n  item: AccordionItem;\n  isOpen: boolean;\n}) {\n  const contentCopy = (\n    <motion.div\n      animate={{\n        opacity: 1,\n        y: 0,\n        scale: 1,\n        filter: \"blur(0px)\",\n      }}\n      className={getQuietContentClassName()}\n      exit={{\n        opacity: 0,\n        y: -2,\n        scale: 0.996,\n        filter: \"blur(1.5px)\",\n      }}\n      initial={{\n        opacity: 0,\n        y: 7,\n        scale: 0.998,\n        filter: \"blur(3px)\",\n      }}\n      transition={contentCopyTransition}\n    >\n      {item.content}\n    </motion.div>\n  );\n\n  return (\n    <AccordionPrimitive.Item className=\"py-3.5\" value={item.id}>\n      <motion.div\n        animate={{ opacity: 1, y: 0 }}\n        initial={{ opacity: 0, y: 12 }}\n        transition={{\n          delay: index * 0.05,\n          duration: 0.3,\n          ease: [0.22, 1, 0.36, 1],\n        }}\n      >\n        <AccordionPrimitive.Header className=\"flex\">\n          <AccordionPrimitive.Trigger className=\"flex w-full cursor-pointer items-baseline gap-4 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\">\n            <span\n              aria-hidden\n              className={cn(\n                \"text-[15px] leading-none transition-colors\",\n                isOpen ? \"text-foreground\" : \"text-muted-foreground/60\"\n              )}\n            >\n              {isOpen ? \"−\" : \"+\"}\n            </span>\n            <span className=\"font-normal text-[15px] text-foreground leading-6 tracking-[-0.02em] sm:text-base\">\n              {item.title}\n            </span>\n          </AccordionPrimitive.Trigger>\n        </AccordionPrimitive.Header>\n\n        <AccordionContent\n          contentCopy={contentCopy}\n          isOpen={isOpen}\n          maskClassName={getQuietContentMaskClassName()}\n          wrapClassName={getQuietContentWrapClassName()}\n        />\n      </motion.div>\n    </AccordionPrimitive.Item>\n  );\n}\n\nexport function Accordion({\n  items,\n  className,\n  multiple = false,\n  variant = \"default\",\n}: AccordionProps) {\n  const [openItems, setOpenItems] = React.useState<string[]>([]);\n  const isQuiet = variant === \"quiet\";\n  const rows = items.map((item, index) =>\n    isQuiet ? (\n      <AccordionQuietRow\n        index={index}\n        isOpen={openItems.includes(item.id)}\n        item={item}\n        key={item.id}\n      />\n    ) : (\n      <AccordionRow\n        index={index}\n        isOpen={openItems.includes(item.id)}\n        item={item}\n        key={item.id}\n      />\n    )\n  );\n\n  if (multiple) {\n    return (\n      <AccordionPrimitive.Root\n        className={cn(\n          componentThemeClassName,\n          \"mx-auto w-full max-w-2xl\",\n          className\n        )}\n        onValueChange={setOpenItems}\n        type=\"multiple\"\n        value={openItems}\n      >\n        {rows}\n      </AccordionPrimitive.Root>\n    );\n  }\n\n  return (\n    <AccordionPrimitive.Root\n      className={cn(\n        componentThemeClassName,\n        \"mx-auto w-full max-w-2xl\",\n        className\n      )}\n      collapsible\n      onValueChange={(value) => setOpenItems(value ? [value] : [])}\n      type=\"single\"\n      value={openItems[0]}\n    >\n      {rows}\n    </AccordionPrimitive.Root>\n  );\n}\n",
      "type": "registry:ui"
    }
  ],
  "title": "Accordion",
  "description": "Accordion with spring height, staggered text, default or quiet disclosure styles, and optional multi-open behavior. Built with Motion."
}
