{
  "example": "import { useState } from \"react\";\nimport { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter } from \"@godxjp/ui/feedback\";\nimport { Button } from \"@godxjp/ui/general\";\n\nfunction CreateDialog() {\n  const [open, setOpen] = useState(false);\n  return (\n    <Dialog open={open} onOpenChange={setOpen}>\n      <DialogTrigger asChild><Button size=\"sm\">新規作成</Button></DialogTrigger>\n      <DialogContent className=\"max-w-lg\">\n        <DialogHeader>\n          <DialogTitle>新規クーポン作成</DialogTitle>\n          <DialogDescription>クーポン情報を入力してください。</DialogDescription>\n        </DialogHeader>\n        {/* The middle MUST be a DialogBody — that is the only scrolling box. Without it, content\n            taller than the viewport is clipped at both ends and the footer goes with it. */}\n        <DialogBody>{/* fields */}</DialogBody>\n        <DialogFooter>\n          <Button variant=\"outline\" onClick={() => setOpen(false)}>キャンセル</Button>\n          <Button onClick={() => setOpen(false)}>保存</Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}",
  "group": "feedback",
  "importPath": "@godxjp/ui/feedback",
  "name": "Dialog",
  "props": [
    {
      "description": "Controlled open state.",
      "name": "open",
      "type": "boolean"
    },
    {
      "description": "Initial open state when uncontrolled.",
      "name": "defaultOpen",
      "type": "boolean"
    },
    {
      "description": "Open-state change handler.",
      "name": "onOpenChange",
      "type": "(open: boolean) => void"
    },
    {
      "defaultValue": "\"default\"",
      "description": "How dangerous this dialog is. ONE prop, THREE results, because they always travel together: `destructive` renders `role=\"alertdialog\"` instead of `role=\"dialog\"`, stops an outside click from dismissing, and gives `DialogAction` the destructive emphasis (antd `okType=\"danger\"`). It also defaults the corner ✕ off, because a ✕ is an accidental-dismiss affordance too. Escape still closes either way. Settable on the root (covers the tree) or on `DialogContent` (the nearer one wins). This is what replaces reaching for the separate `AlertDialog*` family — gh#567.",
      "name": "variant",
      "type": "\"default\" | \"destructive\""
    },
    {
      "defaultValue": "true",
      "description": "`false` renders a NON-MODAL dialog (WAI-ARIA APG allows non-modal dialogs): the page behind stays interactive and in the accessibility tree (no inert/aria-hidden), no scroll lock, no scrim, an outside press does NOT close it, and there is no `aria-modal`. The dialog keeps role=dialog + its title as name, centred fixed placement, sizes and tokens; focus moves into it on open, Tab can leave it, Escape closes it while focus is inside, and focus returns to the trigger on close (only if focus was still inside). Ignored, with a dev warning, under `variant=\"destructive\"` — an alertdialog is always modal. gh#696.",
      "name": "modal",
      "type": "boolean"
    }
  ],
  "related": [
    "AlertDialog — the flat confirm PRESET (title/description/challenge/step-up/pending, no markup of your own). Reach for the preset when it covers the case. When it does not, do NOT reach for the `AlertDialog*` compound parts: use `Dialog` with `variant=\"destructive\"`, which is the same role and the same scrim with a freeform body.",
    "Sheet — use Sheet instead of Dialog when the content is a slide-in panel (filters, detail sidebar, settings drawer). Sheet uses `side` prop and is better suited for wide filter forms or contextual detail panels that don't demand full focus interruption.",
    "Alert — use Alert for inline, non-modal status messages (validation errors, success banners on the page). Dialog is modal and focus-trapping; Alert is inline and never blocks interaction.",
    "Popover — use Popover for lightweight non-modal overlays anchored to a trigger (quick-edit a single field, tooltip-style confirmation for low-stakes actions). Dialog is full-modal; Popover stays near its trigger and doesn't dim the page.",
    "AlertMutationFeedback — use AlertMutationFeedback for toast/inline feedback after the Dialog closes, not inside it. Putting a success toast inside a Dialog that is about to unmount causes it to disappear immediately; emit the feedback after `onOpenChange(false)` resolves."
  ],
  "rules": [
    23,
    3
  ],
  "storyPath": "feedback/dialog.tsx",
  "subParts": [
    "DialogAction",
    "DialogBody",
    "DialogCancel",
    "DialogClose",
    "DialogContent",
    "DialogDescription",
    "DialogFooter",
    "DialogHeader",
    "DialogOverlay",
    "DialogPortal",
    "DialogRoot",
    "DialogTitle",
    "DialogTrigger"
  ],
  "tagline": "Compound modal. Controlled via open + onOpenChange. Parts available flat (DialogTrigger/DialogContent/…) or as Dialog.Trigger/Dialog.Content. Rendered with role=dialog.",
  "usage": [
    "Use `Dialog` for form-style or wizard-style modal flows that need freeform content and a close action.",
    "DO reach for `variant=\"destructive\"` for a dangerous confirmation, INCLUDING one that needs a form inside it (a required reason, a typed challenge). It gives the alertdialog role and the non-dismissable scrim without giving up the freeform body. The separate `AlertDialog*` parts remain for existing code and still work, but they are the older way in.",
    "DO leave `variant` alone for everything that is not destructive. The prop is a danger level, not a colour knob: to tint only the header band use `DialogHeader tone`, which is a separate axis with seven values.",
    "DO always control open state via `open` + `onOpenChange`. Dialog has no uncontrolled shortcut — omitting `open` means the trigger alone drives state, which is fine for simple trigger-only cases, but any async submission flow must use controlled state so you can hold the dialog open while `pending=true` and close it only on success.",
    "DO include `DialogHeader` with `DialogTitle` (and optionally `DialogDescription`) inside every `DialogContent`. a dialog needs an accessible title for screen readers; omitting it triggers a console warning and breaks a11y.",
    "DO wrap tall/scrolling content in `DialogBody` (the ring-safe scroll slot, max-height ~60vh). It insets the content to match the dialog padding so a full-width control's focus ring never clips against the scroll container — mirror of SheetBody.",
    "DO set `modal={false}` when the user must keep working on the page behind an open dialog (edit a list while a payment or detail dialog stays open). Control `open` yourself: an outside press no longer closes it, so give it a visible close action. Escape closes it only while focus is inside the dialog.",
    "DO pass `width` on `DialogContent` for a wide dialog (an overview grid, a comparison): `<DialogContent width=\"60rem\">` (number = px, string = any CSS length; antd Modal `width`, same vocabulary as SheetContent `width`). It is capped at the viewport minus --dialog-viewport-inset, so it never leaves a phone screen. Default 32rem (--dialog-width-default). Do NOT switch to a full-width Sheet to get room (gh#1174).",
    "Enter/exit timing is a token: --dialog-motion-duration (and --sheet-enter-duration / --sheet-exit-duration on Sheet), default var(--duration-fast) = 150ms. Under prefers-reduced-motion both surfaces do not animate at all (gh#1178)."
  ],
  "useCases": [
    "Inline form dialog — create or edit a record (invoice line, supplier, coupon) without navigating away. Place `FormField`/`Input`/`Select` inside `DialogContent`, wire the submit button to your mutation, and hold `open` while `pending` to prevent double-submit.",
    "Read-only detail popup — show a full transaction audit trail, attachment preview, or approval history in a modal without leaving the list page. Use `Dialog` with no `DialogFooter` action buttons, just a close trigger.",
    "Non-modal side task — `modal={false}` keeps a list or cart behind the dialog editable while the dialog stays open (take payment while the order lines can still change).",
    "Wizard / multi-step flow — step through entity setup (legal entity → fiscal year → opening balances) using a single Dialog whose `DialogContent` conditionally renders different step panels. Control which step is shown in local state."
  ]
}
