{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-accordion",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-accordion.tsx",
      "content": "\"use client\";\n\nimport type {\n  AccordionItem as BaseAccordionItemPrimitive,\n  AccordionRoot as BaseAccordionRootPrimitive,\n  AccordionTrigger as BaseAccordionTriggerPrimitive,\n} from \"@base-ui/react/accordion\";\nimport { Accordion as AccordionPrimitive } from \"@base-ui/react/accordion\";\nimport { motion, type Transition, useReducedMotion } 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 AccordionItemData {\n  id: string;\n  title: string;\n  content: React.ReactNode;\n}\n\n/** Legacy alias for the `items` shortcut shape. Prefer `AccordionItemData`. */\nexport type AccordionItem = AccordionItemData;\n\nexport type AccordionVariant = \"default\" | \"quiet\";\n\ntype AccordionValue = string[];\n\ntype AccordionRootPrimitiveProps = Omit<\n  BaseAccordionRootPrimitive.Props<string>,\n  | \"children\"\n  | \"className\"\n  | \"defaultValue\"\n  | \"multiple\"\n  | \"onValueChange\"\n  | \"ref\"\n  | \"value\"\n>;\n\nexport interface AccordionProps extends AccordionRootPrimitiveProps {\n  children?: React.ReactNode;\n  className?: string;\n  collapsible?: boolean;\n  defaultValue?: AccordionValue;\n  items?: AccordionItemData[];\n  multiple?: boolean;\n  onValueChange?: (value: AccordionValue) => void;\n  value?: AccordionValue;\n  variant?: AccordionVariant;\n}\n\ntype AccordionItemPrimitiveProps = Omit<\n  BaseAccordionItemPrimitive.Props,\n  \"className\" | \"ref\" | \"value\"\n>;\n\nexport interface AccordionItemProps extends AccordionItemPrimitiveProps {\n  children?: React.ReactNode;\n  className?: string;\n  value: string;\n}\n\ntype AccordionTriggerPrimitiveProps = Omit<\n  BaseAccordionTriggerPrimitive.Props,\n  \"className\" | \"ref\"\n>;\n\nexport interface AccordionTriggerProps extends AccordionTriggerPrimitiveProps {\n  children?: React.ReactNode;\n  className?: string;\n}\n\ntype AccordionContentBodyProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\"\n>;\n\nexport interface AccordionContentProps extends AccordionContentBodyProps {\n  children?: React.ReactNode;\n  className?: string;\n  /** Keep panel content mounted while closed so height animation can finish cleanly. */\n  forceMount?: boolean;\n  /** Alias for `forceMount`. */\n  keepMounted?: boolean;\n}\n\ntype PanelRenderProps = React.HTMLAttributes<HTMLDivElement> & {\n  children?: React.ReactNode;\n  className?: string;\n  hidden?: boolean;\n  ref?: React.Ref<HTMLDivElement>;\n  style?: React.CSSProperties;\n};\n\nfunction sanitizePanelStyle(style?: React.CSSProperties) {\n  if (!style) {\n    return undefined;\n  }\n\n  const {\n    height: _height,\n    maxHeight: _maxHeight,\n    minHeight: _minHeight,\n    overflow: _overflow,\n    ...rest\n  } = style;\n\n  return Object.keys(rest).length > 0 ? rest : undefined;\n}\n\nfunction resolvePanelProps(panelProps: PanelRenderProps) {\n  const {\n    children: _panelChildren,\n    className: panelClassName,\n    hidden: _panelHidden,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onDrag: _onDrag,\n    onDragEnd: _onDragEnd,\n    onDragEnter: _onDragEnter,\n    onDragExit: _onDragExit,\n    onDragLeave: _onDragLeave,\n    onDragOver: _onDragOver,\n    onDragStart: _onDragStart,\n    onDrop: _onDrop,\n    ref: panelRef,\n    style: panelStyle,\n    ...resolvedPanelProps\n  } = panelProps;\n\n  return {\n    panelClassName,\n    panelRef,\n    panelStyle,\n    resolvedPanelProps,\n  };\n}\n\ntype AccordionRootContextValue = {\n  openItems: AccordionValue;\n  reduceMotion: boolean;\n  variant: AccordionVariant;\n};\n\ntype AccordionItemContextValue = {\n  isOpen: boolean;\n  value: string;\n};\n\nconst AccordionRootContext =\n  React.createContext<AccordionRootContextValue | null>(null);\n\nconst AccordionItemContext =\n  React.createContext<AccordionItemContextValue | null>(null);\n\nconst contentShellTransition: Transition = {\n  height: {\n    type: \"spring\",\n    stiffness: 138,\n    damping: 27,\n    mass: 0.98,\n  },\n  opacity: { duration: 0.22, ease: [0.18, 1, 0.32, 1] },\n};\n\nconst contentCopyTransition: Transition = {\n  opacity: { duration: 0.2, ease: [0.18, 1, 0.32, 1] },\n  y: { duration: 0.24, ease: [0.18, 1, 0.32, 1] },\n};\n\nconst instantTransition: Transition = { duration: 0 };\n\nconst triggerBaseClassName =\n  \"flex w-full cursor-pointer text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50\";\n\nfunction normalizeAccordionValue(value: AccordionValue | undefined) {\n  const items = (value ?? []).filter((item) => item.length > 0);\n\n  return items.filter((item, index) => items.indexOf(item) === index);\n}\n\nfunction getSingleOrMultipleValue(value: AccordionValue, multiple: boolean) {\n  return multiple ? value : value.slice(0, 1);\n}\n\nfunction getShellTransition(reduceMotion: boolean): Transition {\n  return reduceMotion ? instantTransition : contentShellTransition;\n}\n\nfunction getCopyTransition(reduceMotion: boolean): Transition {\n  return reduceMotion ? instantTransition : contentCopyTransition;\n}\n\nfunction useAccordionRootContext(componentName: string) {\n  const context = React.useContext(AccordionRootContext);\n\n  if (!context) {\n    throw new Error(`${componentName} must be used inside Accordion.`);\n  }\n\n  return context;\n}\n\nfunction useAccordionItemContext(componentName: string) {\n  const context = React.useContext(AccordionItemContext);\n\n  if (!context) {\n    throw new Error(`${componentName} must be used inside AccordionItem.`);\n  }\n\n  return context;\n}\n\nfunction getItemClassName(variant: AccordionVariant) {\n  if (variant === \"quiet\") {\n    return \"py-3.5\";\n  }\n\n  return \"group [&:not(:first-child)]:border-border/80 [&:not(:first-child)]: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 getContentCopyClassName() {\n  return \"space-y-3 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 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 AccordionPanelBody({\n  children,\n  contentProps,\n  contentRef,\n  copyClassName,\n  isOpen,\n  reduceMotion,\n  wrapClassName,\n}: {\n  children: React.ReactNode;\n  contentProps?: Omit<\n    React.HTMLAttributes<HTMLDivElement>,\n    \"children\" | \"className\"\n  >;\n  contentRef?: React.Ref<HTMLDivElement>;\n  copyClassName: string;\n  isOpen: boolean;\n  reduceMotion: boolean;\n  wrapClassName: string;\n}) {\n  if (reduceMotion) {\n    if (!isOpen) {\n      return null;\n    }\n\n    return (\n      <div className=\"overflow-hidden\">\n        <div {...contentProps} className={wrapClassName} ref={contentRef}>\n          <div className={copyClassName}>{children}</div>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <motion.div\n      animate={{ height: isOpen ? \"auto\" : 0, opacity: isOpen ? 1 : 0 }}\n      className=\"overflow-hidden\"\n      initial={false}\n      transition={getShellTransition(reduceMotion)}\n    >\n      <div {...contentProps} className={wrapClassName} ref={contentRef}>\n        <motion.div\n          animate={{ opacity: isOpen ? 1 : 0, y: isOpen ? 0 : -4 }}\n          aria-hidden={!isOpen}\n          className={copyClassName}\n          inert={isOpen ? undefined : true}\n          initial={false}\n          transition={getCopyTransition(reduceMotion)}\n        >\n          {children}\n        </motion.div>\n      </div>\n    </motion.div>\n  );\n}\n\nfunction AccordionTriggerLabel({ children }: { children: React.ReactNode }) {\n  return (\n    <span className=\"pr-4 font-medium text-[15px] text-foreground leading-6 tracking-[-0.02em] sm:text-base\">\n      {children}\n    </span>\n  );\n}\n\nfunction AccordionTriggerIndicator({\n  isOpen,\n  reduceMotion,\n}: {\n  isOpen: boolean;\n  reduceMotion: boolean;\n}) {\n  if (reduceMotion) {\n    return (\n      <span\n        aria-hidden\n        className=\"mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center text-foreground opacity-80\"\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          {isOpen ? null : (\n            <span className=\"absolute h-3 w-px rounded-full bg-current\" />\n          )}\n        </span>\n      </span>\n    );\n  }\n\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      initial={false}\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          initial={false}\n          transition={{\n            duration: 0.18,\n            ease: [0.22, 1, 0.36, 1],\n          }}\n        />\n      </span>\n    </motion.div>\n  );\n}\n\nconst AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(\n  ({ children, className, value, ...props }, ref) => {\n    const { openItems, variant } = useAccordionRootContext(\"AccordionItem\");\n    const isOpen = openItems.includes(value);\n    const itemContext = React.useMemo(\n      () => ({ isOpen, value }),\n      [isOpen, value]\n    );\n\n    return (\n      <AccordionItemContext.Provider value={itemContext}>\n        <AccordionPrimitive.Item\n          className={cn(getItemClassName(variant), className)}\n          data-slot=\"accordion-item\"\n          ref={ref}\n          value={value}\n          {...props}\n        >\n          {children}\n        </AccordionPrimitive.Item>\n      </AccordionItemContext.Provider>\n    );\n  }\n);\n\nAccordionItem.displayName = \"AccordionItem\";\n\nconst AccordionTrigger = React.forwardRef<HTMLElement, AccordionTriggerProps>(\n  ({ children, className, ...props }, ref) => {\n    const { reduceMotion, variant } =\n      useAccordionRootContext(\"AccordionTrigger\");\n    const { isOpen } = useAccordionItemContext(\"AccordionTrigger\");\n\n    if (variant === \"quiet\") {\n      return (\n        <AccordionPrimitive.Header className=\"flex\">\n          <AccordionPrimitive.Trigger\n            className={cn(\n              triggerBaseClassName,\n              \"items-baseline gap-4\",\n              className\n            )}\n            data-slot=\"accordion-trigger\"\n            ref={ref}\n            {...props}\n          >\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              {children}\n            </span>\n          </AccordionPrimitive.Trigger>\n        </AccordionPrimitive.Header>\n      );\n    }\n\n    return (\n      <AccordionPrimitive.Header className=\"flex\">\n        <AccordionPrimitive.Trigger\n          className={cn(\n            triggerBaseClassName,\n            \"items-start justify-between gap-6\",\n            getTriggerClassName(isOpen),\n            className\n          )}\n          data-slot=\"accordion-trigger\"\n          ref={ref}\n          {...props}\n        >\n          <AccordionTriggerLabel>{children}</AccordionTriggerLabel>\n          <AccordionTriggerIndicator\n            isOpen={isOpen}\n            reduceMotion={reduceMotion}\n          />\n        </AccordionPrimitive.Trigger>\n      </AccordionPrimitive.Header>\n    );\n  }\n);\n\nAccordionTrigger.displayName = \"AccordionTrigger\";\n\nconst AccordionContent = React.forwardRef<\n  HTMLDivElement,\n  AccordionContentProps\n>(({ children, className, forceMount, keepMounted, ...props }, ref) => {\n  const { reduceMotion, variant } = useAccordionRootContext(\"AccordionContent\");\n  const isQuiet = variant === \"quiet\";\n  const shouldKeepMounted = keepMounted ?? forceMount ?? true;\n  const copyClassNameBase = isQuiet\n    ? getQuietContentClassName()\n    : getContentCopyClassName();\n  const wrapClassName = isQuiet\n    ? getQuietContentWrapClassName()\n    : getContentWrapClassName();\n\n  return (\n    <AccordionPrimitive.Panel\n      data-slot=\"accordion-content\"\n      keepMounted={shouldKeepMounted}\n      render={(panelProps, state) => {\n        const isOpen = state.open;\n        const { panelClassName, panelRef, panelStyle, resolvedPanelProps } =\n          resolvePanelProps(panelProps);\n\n        return (\n          <div\n            {...resolvedPanelProps}\n            className={cn(\"overflow-hidden\", panelClassName)}\n            ref={panelRef}\n            style={sanitizePanelStyle(panelStyle)}\n          >\n            <AccordionPanelBody\n              contentProps={props}\n              contentRef={ref}\n              copyClassName={cn(copyClassNameBase, className)}\n              isOpen={isOpen}\n              reduceMotion={reduceMotion}\n              wrapClassName={wrapClassName}\n            >\n              {children}\n            </AccordionPanelBody>\n          </div>\n        );\n      }}\n    />\n  );\n});\n\nAccordionContent.displayName = \"AccordionContent\";\n\nconst Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(\n  (\n    {\n      children,\n      className,\n      collapsible = true,\n      defaultValue,\n      items,\n      multiple = false,\n      onValueChange,\n      value,\n      variant = \"default\",\n      ...props\n    },\n    ref\n  ) => {\n    const reduceMotion = useReducedMotion() === true;\n    const isControlled = value !== undefined;\n    const [uncontrolledValue, setUncontrolledValue] =\n      React.useState<AccordionValue>(() =>\n        getSingleOrMultipleValue(\n          normalizeAccordionValue(defaultValue),\n          multiple\n        )\n      );\n    const openItems = getSingleOrMultipleValue(\n      normalizeAccordionValue(isControlled ? value : uncontrolledValue),\n      multiple\n    );\n\n    React.useEffect(() => {\n      if (isControlled) {\n        return;\n      }\n\n      setUncontrolledValue((current) =>\n        getSingleOrMultipleValue(normalizeAccordionValue(current), multiple)\n      );\n    }, [isControlled, multiple]);\n\n    const handleValueChange = React.useCallback(\n      (nextValue: AccordionValue) => {\n        const normalizedValue = getSingleOrMultipleValue(\n          normalizeAccordionValue(nextValue),\n          multiple\n        );\n\n        if (\n          !(multiple || collapsible) &&\n          normalizedValue.length === 0 &&\n          openItems.length > 0\n        ) {\n          return;\n        }\n\n        if (!isControlled) {\n          setUncontrolledValue(normalizedValue);\n        }\n\n        onValueChange?.(normalizedValue);\n      },\n      [collapsible, isControlled, multiple, onValueChange, openItems.length]\n    );\n\n    const rootContext = React.useMemo(\n      () => ({ openItems, reduceMotion, variant }),\n      [openItems, reduceMotion, variant]\n    );\n\n    const accordionChildren = items\n      ? items.map((item) => (\n          <AccordionItem key={item.id} value={item.id}>\n            <AccordionTrigger>{item.title}</AccordionTrigger>\n            <AccordionContent>{item.content}</AccordionContent>\n          </AccordionItem>\n        ))\n      : children;\n\n    return (\n      <AccordionRootContext.Provider value={rootContext}>\n        <AccordionPrimitive.Root\n          className={cn(\n            componentThemeClassName,\n            \"mx-auto w-full max-w-2xl\",\n            className\n          )}\n          data-slot=\"accordion\"\n          multiple={multiple}\n          onValueChange={(nextValue) => handleValueChange(nextValue)}\n          ref={ref}\n          value={openItems}\n          {...props}\n        >\n          {accordionChildren}\n        </AccordionPrimitive.Root>\n      </AccordionRootContext.Provider>\n    );\n  }\n);\n\nAccordion.displayName = \"Accordion\";\n\nexport { Accordion, AccordionContent, AccordionItem, AccordionTrigger };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Accordion (Base UI)",
  "description": "Compound Accordion, AccordionItem, AccordionTrigger, and AccordionContent parts layered over Base UI primitives with default and quiet variants."
}
