{
  "schemaVersion": 1,
  "target": "react",
  "name": "dialog",
  "version": "0.2.0",
  "category": "core",
  "title": "Dialog",
  "description": "A modal window built on Base UI Dialog, with a backdrop, focus trap, a footer action band, and a 220ms enter and faster 150ms exit.",
  "tags": [
    "overlay",
    "modal",
    "dialog",
    "base-ui"
  ],
  "requires": {
    "projectTypes": [
      "next",
      "vite-react"
    ],
    "styling": "tailwind-v4"
  },
  "npmDependencies": {
    "@base-ui/react": "^1.9.0"
  },
  "registryDependencies": [],
  "docsPath": "/components/dialog",
  "props": [
    {
      "name": "Dialog open / defaultOpen / onOpenChange",
      "type": "boolean / boolean / (open, details) => void",
      "description": "Root state. Leave open unset for an uncontrolled dialog."
    },
    {
      "name": "Dialog modal",
      "type": "boolean | \"trap-focus\"",
      "default": "true",
      "description": "Traps focus, locks page scroll, and blocks outside pointer interaction."
    },
    {
      "name": "Dialog disablePointerDismissal",
      "type": "boolean",
      "default": "false",
      "description": "Keeps the dialog open when the backdrop is clicked."
    },
    {
      "name": "DialogTrigger",
      "type": "Base UI Dialog.Trigger props",
      "description": "Unstyled button that opens the dialog. Pass className, or render={<Button />} to reuse your own button."
    },
    {
      "name": "DialogContent size",
      "type": "\"sm\" | \"md\" | \"lg\"",
      "default": "\"md\"",
      "description": "Maximum width of the panel."
    },
    {
      "name": "DialogContent showCloseButton",
      "type": "boolean",
      "default": "true",
      "description": "Renders a corner icon button that closes the dialog."
    },
    {
      "name": "DialogContent closeLabel",
      "type": "string",
      "default": "\"Close\"",
      "description": "Accessible name of the corner close button."
    },
    {
      "name": "DialogContent initialFocus / finalFocus",
      "type": "boolean | RefObject | function",
      "description": "Overrides where focus goes on open and on close. Defaults to the first tabbable element and the trigger."
    },
    {
      "name": "DialogContent backdropClassName",
      "type": "string",
      "description": "Classes for the backdrop."
    },
    {
      "name": "DialogTitle / DialogDescription",
      "type": "Base UI Dialog.Title / Dialog.Description props",
      "description": "Heading and supporting text, wired to aria-labelledby and aria-describedby."
    },
    {
      "name": "DialogHeader / DialogFooter",
      "type": "ComponentProps<\"div\">",
      "description": "Layout wrappers for the title block and the action row. DialogFooter sits in a band under a divider at the bottom of the panel and assumes the default p-6 padding."
    },
    {
      "name": "DialogClose",
      "type": "Base UI Dialog.Close props",
      "description": "Unstyled button that closes the dialog, for Cancel or Done actions."
    }
  ],
  "accessibility": [
    "The panel has role=\"dialog\" and aria-modal, and is named by DialogTitle and described by DialogDescription.",
    "Opening moves focus into the dialog, Tab is trapped inside it, and closing returns focus to the trigger.",
    "Escape and a click on the backdrop close the dialog; page scroll is locked while it is open.",
    "The corner close button has an accessible name (closeLabel) and a 32px target; its icon is hidden from screen readers.",
    "With reduced motion the fade, scale, and slide transitions are removed and the dialog appears and disappears immediately."
  ],
  "states": [
    "closed",
    "open"
  ],
  "files": [
    {
      "path": "ui/dialog.tsx",
      "content": "\"use client\";\n\nimport { Dialog as BaseDialog } from \"@base-ui/react/dialog\";\nimport type { ComponentProps } from \"react\";\n\ntype ClassName<State> = string | ((state: State) => string | undefined) | undefined;\n\nfunction cx(...classes: (string | false | null | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\n\n/** Prepends base classes while keeping Base UI's state-function form of className working. */\nfunction withBase<State>(base: string, className: ClassName<State>) {\n  return typeof className === \"function\" ? (state: State) => cx(base, className(state)) : cx(base, className);\n}\n\n/** Groups the dialog parts and holds its open state. Pass `open`/`onOpenChange` to control it. */\nexport const Dialog = BaseDialog.Root;\n\n/** A button that opens the dialog. Unstyled; pass `className`, or `render={<Button />}` to reuse your button. */\nexport const DialogTrigger = BaseDialog.Trigger;\n\n/** A button that closes the dialog. Unstyled; pass `className` or `render`. */\nexport const DialogClose = BaseDialog.Close;\n\nconst sizes = {\n  sm: \"max-w-sm\",\n  md: \"max-w-md\",\n  lg: \"max-w-xl\",\n} as const;\n\nexport type DialogSize = keyof typeof sizes;\n\nexport interface DialogContentProps extends BaseDialog.Popup.Props {\n  /** Maximum width of the panel. */\n  size?: DialogSize;\n  /** Renders an icon button in the corner that closes the dialog. */\n  showCloseButton?: boolean;\n  /** Accessible name of the corner close button. */\n  closeLabel?: string;\n  /** Classes for the backdrop behind the panel. */\n  backdropClassName?: string;\n  /** Element to portal into. Defaults to `document.body`. */\n  container?: BaseDialog.Portal.Props[\"container\"];\n}\n\n/** The dialog panel, rendered in a portal above a backdrop. */\nexport function DialogContent({\n  size = \"md\",\n  showCloseButton = true,\n  closeLabel = \"Close\",\n  backdropClassName,\n  container,\n  className,\n  children,\n  ...props\n}: DialogContentProps) {\n  return (\n    <BaseDialog.Portal container={container}>\n      <BaseDialog.Backdrop\n        className={cx(\n          \"fixed inset-0 z-50 bg-black/40 dark:bg-black/70\",\n          \"transition-opacity duration-220 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none\",\n          \"data-[starting-style]:opacity-0 data-[ending-style]:opacity-0 data-[ending-style]:duration-150\",\n          backdropClassName,\n        )}\n      />\n      <BaseDialog.Popup\n        className={withBase(\n          cx(\n            // Centered with inset + auto margins so `translate` stays free for the motion.\n            \"fixed inset-0 z-50 m-auto grid h-fit max-h-[calc(100dvh-2rem)] w-[calc(100vw-2rem)] gap-5 overflow-y-auto overscroll-contain p-6\",\n            \"rounded-2xl border border-zinc-950/10 bg-white text-zinc-950 shadow-[0_24px_64px_-16px_rgb(0_0_0/0.28),0_2px_6px_rgb(0_0_0/0.04)]\",\n            \"dark:border-white/15 dark:bg-black dark:text-white dark:shadow-[0_24px_64px_-12px_rgb(0_0_0/1),inset_0_1px_0_rgb(255_255_255/0.08)]\",\n            \"outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500\",\n            // Enter: 220ms quint ease-out from 96% and 8px low. Exit: 150ms, a smaller move.\n            \"transition-[opacity,scale,translate] duration-220 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none\",\n            \"data-[starting-style]:opacity-0 motion-safe:data-[starting-style]:translate-y-2 motion-safe:data-[starting-style]:scale-96\",\n            \"data-[ending-style]:opacity-0 data-[ending-style]:duration-150 motion-safe:data-[ending-style]:translate-y-1 motion-safe:data-[ending-style]:scale-98\",\n            // A dialog opened on top of this one pushes it back slightly.\n            \"motion-safe:data-[nested-dialog-open]:scale-[0.97] data-[nested-dialog-open]:opacity-60\",\n            sizes[size],\n          ),\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        {showCloseButton ? (\n          <BaseDialog.Close\n            aria-label={closeLabel}\n            className={cx(\n              \"absolute top-4 right-4 inline-flex size-8 items-center justify-center rounded-lg text-zinc-500\",\n              \"transition-[background-color,color,scale] duration-150 ease-out active:duration-100 motion-reduce:transition-none\",\n              \"hover:bg-zinc-950/[0.05] hover:text-zinc-950 motion-safe:active:scale-[0.94]\",\n              \"dark:hover:bg-white/[0.08] dark:hover:text-white\",\n              \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500\",\n            )}\n          >\n            <svg aria-hidden=\"true\" viewBox=\"0 0 16 16\" fill=\"none\" className=\"size-4\">\n              <path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n            </svg>\n          </BaseDialog.Close>\n        ) : null}\n      </BaseDialog.Popup>\n    </BaseDialog.Portal>\n  );\n}\n\n/** Wraps the title and description. Leaves room for the corner close button. */\nexport function DialogHeader({ className, ...props }: ComponentProps<\"div\">) {\n  return <div className={cx(\"grid gap-1.5 pr-10\", className)} {...props} />;\n}\n\n/**\n * Holds the actions, right-aligned on wider screens and stacked on narrow ones. Sits in a band\n * under a divider at the bottom of the panel; it assumes the panel's default `p-6` padding.\n */\nexport function DialogFooter({ className, ...props }: ComponentProps<\"div\">) {\n  return (\n    <div\n      className={cx(\n        \"-mx-6 -mb-6 mt-1 flex flex-col-reverse gap-2 border-t border-zinc-950/[0.07] bg-zinc-950/[0.015] px-6 py-4 sm:flex-row sm:justify-end\",\n        \"dark:border-white/[0.08] dark:bg-transparent\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\n/** The dialog's heading. Labels the dialog with aria-labelledby. */\nexport function DialogTitle({ className, ...props }: BaseDialog.Title.Props) {\n  return <BaseDialog.Title className={withBase(\"text-base leading-6 font-semibold tracking-[-0.01em]\", className)} {...props} />;\n}\n\n/** Supporting text. Describes the dialog with aria-describedby. */\nexport function DialogDescription({ className, ...props }: BaseDialog.Description.Props) {\n  return (\n    <BaseDialog.Description className={withBase(\"text-sm leading-relaxed text-zinc-600 dark:text-zinc-400\", className)} {...props} />\n  );\n}\n"
    }
  ],
  "example": {
    "content": "import {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n  DialogTrigger,\n} from \"@/components/seamless/ui/dialog\";\n\n// Same recipe as the Button component, inlined so this example has no other dependency.\nconst button =\n  \"inline-flex h-9 items-center justify-center rounded-lg px-3.5 text-sm font-medium select-none \" +\n  \"transition-[background-color,border-color,scale] duration-150 ease-out active:duration-100 motion-safe:active:scale-[0.97] motion-reduce:transition-none \" +\n  \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500\";\nconst primary =\n  `${button} bg-zinc-950 text-white shadow-[inset_0_1px_0_rgb(255_255_255/0.12),0_1px_2px_rgb(0_0_0/0.2)] hover:bg-zinc-800 ` +\n  \"dark:bg-white dark:text-black dark:shadow-[inset_0_-1px_0_rgb(0_0_0/0.12)] dark:hover:bg-zinc-200\";\nconst secondary =\n  `${button} border border-zinc-950/12 bg-white text-zinc-950 hover:border-zinc-950/20 hover:bg-zinc-950/[0.03] ` +\n  \"dark:border-white/15 dark:bg-black dark:text-white dark:hover:border-white/25 dark:hover:bg-white/[0.06]\";\n\nexport default function DialogExample() {\n  return (\n    <Dialog>\n      <DialogTrigger className={secondary}>Rename workspace</DialogTrigger>\n      <DialogContent>\n        <DialogHeader>\n          <DialogTitle>Rename workspace</DialogTitle>\n          <DialogDescription>Everyone on your team will see the new name.</DialogDescription>\n        </DialogHeader>\n        <label className=\"grid gap-1.5 text-sm font-medium\">\n          Name\n          <input\n            defaultValue=\"Acme\"\n            className={\n              \"h-9 rounded-lg border border-zinc-950/15 bg-white px-3 text-sm font-normal text-zinc-950 \" +\n              \"transition-[border-color,box-shadow] duration-150 motion-reduce:transition-none \" +\n              \"focus-visible:border-sky-500 focus-visible:ring-3 focus-visible:ring-sky-500/20 focus-visible:outline-none \" +\n              \"dark:border-white/15 dark:bg-black dark:text-white\"\n            }\n          />\n        </label>\n        <DialogFooter>\n          <DialogClose className={secondary}>Cancel</DialogClose>\n          <DialogClose className={primary}>Save</DialogClose>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n"
  }
}
