{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"collapsible","type":"registry:component","title":"Collapsible","description":"\"Show 4 more\" that opens in place: the hidden part eases open through a blur while the button's words morph to \"Show less\".","author":"Hairline UI","dependencies":[],"registryDependencies":["https://hairlineui.com/r/accordion.json?as=dependency","https://hairlineui.com/r/text-morph.json?as=dependency","https://hairlineui.com/r/ui-interaction-motion.json?as=dependency"],"files":[{"path":"registry/collapsible.tsx","type":"registry:component","content":"// Hairline UI · collapsible · MIT License\n// Copyright (c) 2026 Hairline UI · https://hairlineui.com/license#components\n\n\"use client\";\n\nimport * as React from \"react\";\nimport { Chevron, Fold } from \"./accordion\";\nimport { TextMorph } from \"./text-morph\";\nimport { INTERACTION_FOCUS, INTERACTION_PRESS, useInteractionReducedMotion } from \"./ui-interaction-motion\";\n\n/* ─────────────────────────────────────────────────────────\n * COLLAPSIBLE: \"Show 4 more\" that opens in place\n *\n *   open     the hidden part's height eases open while its content\n *            rises out of a blur; the button's words morph\n *            (\"Show 4 more\" → \"Show less\") and its chevron turns\n *   close    the reverse; the button stays where your finger is\n *            when it sits above the content (`placement=\"top\"`)\n *\n * One disclosure: aria-expanded on the button, the content a\n * region it controls, folded content inert.\n * ───────────────────────────────────────────────────────── */\n\nexport interface CollapsibleProps {\n  children: React.ReactNode;\n  /** Words on the button when closed and open. */\n  labels?: { closed: string; open: string };\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Button above the hidden part (details) or below it (\"show more\" lists). */\n  placement?: \"top\" | \"bottom\";\n  className?: string;\n}\n\nexport function Collapsible({ children, labels = { closed: \"Show more\", open: \"Show less\" }, open: controlled, defaultOpen = false, onOpenChange, placement = \"bottom\", className = \"\" }: CollapsibleProps) {\n  const reduced = useInteractionReducedMotion();\n  const id = React.useId();\n  const [own, setOwn] = React.useState(defaultOpen);\n  const open = controlled ?? own;\n\n  const button = (\n    <button\n      type=\"button\"\n      aria-expanded={open}\n      aria-controls={id}\n      onClick={() => {\n        if (controlled === undefined) setOwn(!open);\n        onOpenChange?.(!open);\n      }}\n      className={`-ml-3 inline-flex h-8 items-center gap-1.5 self-start rounded-full px-3 text-[13px] font-medium text-foreground cursor-pointer hover:bg-accent ${INTERACTION_PRESS} ${INTERACTION_FOCUS}`}\n    >\n      <TextMorph>{open ? labels.open : labels.closed}</TextMorph>\n      <Chevron open={open} reduced={reduced} />\n    </button>\n  );\n\n  return (\n    <div className={`flex w-full flex-col gap-1 ${className}`}>\n      {placement === \"top\" && button}\n      <Fold open={open} reduced={reduced} id={id}>\n        {children}\n      </Fold>\n      {placement === \"bottom\" && button}\n    </div>\n  );\n}\n\nexport default Collapsible;\n"}],"css":{},"docs":"Docs and live preview: https://hairlineui.com/ui/collapsible. MIT licensed: https://hairlineui.com/license#components","meta":{"license":"MIT"}}