{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dialog",
  "title": "Dialog",
  "description": "Centered modal with a spring-scaled popup and fading backdrop.",
  "dependencies": [
    "motion",
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "springs",
    "button"
  ],
  "files": [
    {
      "path": "src/components/ui/dialog.tsx",
      "content": "\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { Dialog as DialogPrimitive } from \"@base-ui/react/dialog\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { XIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { spring } from \"@/lib/springs\";\nimport { Button } from \"@/components/ui/button\";\n\nfunction Dialog({ ...props }: DialogPrimitive.Root.Props) {\n  return <DialogPrimitive.Root data-slot=\"dialog\" {...props} />;\n}\n\nfunction DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {\n  return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogClose({ ...props }: DialogPrimitive.Close.Props) {\n  return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {\n  return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\n/**\n * Rendered through Base UI's `render` function-prop rather than a plain\n * `className` so the backdrop can read `state.transitionStatus` — Base UI\n * keeps the element mounted through a \"ending\" status and only removes it\n * once it detects the animation on it has finished, so handing that status\n * to motion (instead of a CSS transition) is enough to get a correct\n * exit with no manual mount-tracking, unlike Accordion's Panel (which\n * doesn't defer removal the same way).\n */\nfunction DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {\n  const reduceMotion = useReducedMotion();\n\n  return (\n    <DialogPrimitive.Backdrop\n      data-slot=\"dialog-overlay\"\n      render={(backdropProps, state) => {\n        const exiting = state.transitionStatus === \"ending\";\n        return (\n          <motion.div\n            {...(backdropProps as Record<string, unknown>)}\n            className={cn(\"fixed inset-0 z-50 bg-black/40 backdrop-blur-sm\", className)}\n            initial={{ opacity: 0 }}\n            animate={{ opacity: exiting ? 0 : 1 }}\n            transition={\n              reduceMotion ? spring.fast.enter : exiting ? spring.slow.exit : spring.slow.enter\n            }\n          />\n        );\n      }}\n      {...props}\n    />\n  );\n}\n\n/**\n * Centered, fully-rounded surface — unlike Drawer (which sits flush against\n * a viewport edge), a dialog is a detached floating plane, so it gets the\n * `--popover` step and `shadow-panel` on every side rather than Drawer's\n * edge-attached treatment.\n */\nfunction DialogContent({\n  className,\n  children,\n  size = \"sm\",\n  showCloseButton = true,\n  ...props\n}: DialogPrimitive.Popup.Props & {\n  size?: \"sm\" | \"lg\";\n  showCloseButton?: boolean;\n}) {\n  const reduceMotion = useReducedMotion();\n  const restingOffset = reduceMotion ? \"-50%\" : \"calc(-50% + 12px)\";\n\n  return (\n    <DialogPortal>\n      <DialogOverlay />\n      <DialogPrimitive.Popup\n        data-slot=\"dialog-content\"\n        render={(popupProps, state) => {\n          const exiting = state.transitionStatus === \"ending\";\n          return (\n            <motion.div\n              {...(popupProps as Record<string, unknown>)}\n              {...(props as Record<string, unknown>)}\n              className={cn(\n                \"fixed top-1/2 left-1/2 z-50 flex w-[calc(100%-2rem)] flex-col rounded-xl bg-popover p-6 text-popover-foreground shadow-panel outline-none\",\n                size === \"sm\" && \"max-w-sm\",\n                size === \"lg\" && \"max-w-lg\",\n                className,\n              )}\n              initial={{ opacity: 0, x: \"-50%\", y: restingOffset }}\n              animate={{\n                opacity: exiting ? 0 : 1,\n                x: \"-50%\",\n                y: exiting ? restingOffset : \"-50%\",\n              }}\n              transition={\n                reduceMotion\n                  ? spring.fast.enter\n                  : exiting\n                    ? spring.slow.exit\n                    : { ...spring.slow.enter, delay: 0.06 }\n              }\n            >\n              {children}\n              {showCloseButton && (\n                <DialogPrimitive.Close\n                  data-slot=\"dialog-close\"\n                  render={\n                    <Button\n                      variant=\"ghost\"\n                      // Ghost's default hover:bg-muted is calibrated against\n                      // --background; on --popover it's nearly invisible in\n                      // dark mode (0.3 vs muted's 0.295 lightness — a ~0.005\n                      // step). A foreground-tinted overlay contrasts against\n                      // any surface instead of matching one particular token.\n                      className=\"absolute top-3 right-3 hover:bg-foreground/10 dark:hover:bg-foreground/10\"\n                      size=\"icon-sm\"\n                    />\n                  }\n                >\n                  <XIcon />\n                  <span className=\"sr-only\">Close</span>\n                </DialogPrimitive.Close>\n              )}\n            </motion.div>\n          );\n        }}\n      />\n    </DialogPortal>\n  );\n}\n\nfunction DialogHeader({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div data-slot=\"dialog-header\" className={cn(\"flex flex-col gap-1.5\", className)} {...props} />\n  );\n}\n\nfunction DialogFooter({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"dialog-footer\"\n      className={cn(\"mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {\n  return (\n    <DialogPrimitive.Title\n      data-slot=\"dialog-title\"\n      className={cn(\"text-control font-medium text-foreground\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) {\n  return (\n    <DialogPrimitive.Description\n      data-slot=\"dialog-description\"\n      className={cn(\"text-caption text-muted-foreground\", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Dialog,\n  DialogTrigger,\n  DialogClose,\n  DialogPortal,\n  DialogOverlay,\n  DialogContent,\n  DialogHeader,\n  DialogFooter,\n  DialogTitle,\n  DialogDescription,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}