# Dialog

> Modal dialog for short focused tasks and confirmations, with backdrop, focus trap and a built-in close button. Wraps Base UI Dialog.

Source: https://docs.nasaqui.com/components/dialog

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/dialog.json
```

A centred modal for one short task: rename something, confirm a destructive action. It blocks the page
behind a backdrop, traps focus, and closes on `Esc` or an outside press. The parts are thin styled wrappers
over Base UI `Dialog`; `Dialog`, `DialogTrigger` and `DialogClose` are the Base UI parts unchanged.

## When to use

- A confirmation ("Delete this project?") or a small form (one to three fields).
- A decision the user must make before continuing.

## When not to use

- Editing a record with many fields, or content that should sit beside the page: use [`Sheet`](https://docs.nasaqui.com/components/sheet).
- A list of actions: use [`DropdownMenu`](https://docs.nasaqui.com/components/dropdown-menu).
- Feedback after an action ("Saved"): use [`toast`](https://docs.nasaqui.com/components/toast).

## Import

```tsx
import {
  Dialog, DialogTrigger, DialogClose, DialogContent,
  DialogHeader, DialogTitle, DialogDescription, DialogFooter,
} from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import {
  Button, Dialog, DialogClose, DialogContent, DialogDescription,
  DialogFooter, DialogHeader, DialogTitle, DialogTrigger,
} from "@fadymondy/nasaq/web";

export function DeleteProject() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="danger" />}>Delete project</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Delete this project?</DialogTitle>
          <DialogDescription>Issues, time entries and files are removed. This cannot be undone.</DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Keep project</DialogClose>
          <DialogClose render={<Button variant="danger" />}>Delete</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

## Anatomy

```
Dialog                          Base UI Dialog.Root (no DOM)
├─ DialogTrigger                Base UI Dialog.Trigger (a button; use render={<Button />})
└─ DialogContent                Portal + backdrop + popup   data-slot="dialog-content"
   ├─ DialogBackdrop            data-slot="dialog-backdrop" (rendered for you)
   ├─ DialogHeader              data-slot="dialog-header"
   │  ├─ DialogTitle            data-slot="dialog-title"
   │  └─ DialogDescription      data-slot="dialog-description"
   ├─ your content
   ├─ DialogFooter              data-slot="dialog-footer"
   │  └─ DialogClose            Base UI Dialog.Close
   └─ close (×) button          data-slot="dialog-close" (when showClose)
```

## API

### `Dialog`, `DialogTrigger`, `DialogClose`

Aliases of Base UI `Dialog.Root`, `Dialog.Trigger`, `Dialog.Close`. Common `Dialog` props:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open?` | `boolean` | none | Controlled open state. |
| `defaultOpen?` | `boolean` | `false` | Initial state when uncontrolled. |
| `onOpenChange?` | `(open: boolean, details) => void` | none | Called when it opens or closes. |
| `modal?` | `boolean \| "trap-focus"` | `true` | Base UI modal behaviour. |
| `disablePointerDismissal?` | `boolean` | `false` | Ignore outside presses. |

`DialogTrigger` and `DialogClose` accept `render` to render as a `Button`.

### `DialogContent`

`DialogContentProps` extends Base UI `Dialog.Popup` props.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `showClose?` | `boolean` | `true` | Renders the × button at the inline-end top corner. |
| `closeLabel?` | `string` | `"Close"` / `"إغلاق"` by locale | `aria-label` of the × button. Defaults from the Nasaq locale (`ar*` gives Arabic); pass your own to override. |
| `className?` | `string` | none | Merged onto the popup. |
| `children?` | `ReactNode` | none | Header, body, footer. |

The popup is `fixed inset-0 m-auto`, `max-w-lg`, `w-[calc(100%-2rem)]`, and scrolls when taller than the viewport.

### `DialogBackdrop`

Takes Base UI `Dialog.Backdrop` props. Rendered by `DialogContent`; exported for custom compositions.

### `DialogHeader`, `DialogFooter`

`ComponentProps<"div">`. The header reserves `pe-8` for the close button. The footer is a reversed column on
mobile and an end-aligned row from `sm`.

### `DialogTitle`, `DialogDescription`

Base UI `Dialog.Title` / `Dialog.Description` props. Base UI links them to the popup as its accessible name and description.

## Examples

### Small form

```tsx
import {
  Button, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter,
  DialogHeader, DialogTitle, DialogTrigger, Field, FieldLabel, Input,
} from "@fadymondy/nasaq/web";

export function RenameProject() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="primary" />}>Rename project</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Rename project</DialogTitle>
          <DialogDescription>The new name shows everywhere the project appears.</DialogDescription>
        </DialogHeader>
        <Field>
          <FieldLabel>Name</FieldLabel>
          <Input defaultValue="Nasaq" />
        </Field>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Cancel</DialogClose>
          <DialogClose render={<Button variant="primary" />}>Save</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

### Controlled, Arabic copy

```tsx
import {
  Button, Dialog, DialogClose, DialogContent, DialogDescription,
  DialogFooter, DialogHeader, DialogTitle,
} from "@fadymondy/nasaq/web";
import { useState } from "react";

export function ConfirmAr() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <Button variant="danger" onClick={() => setOpen(true)}>حذف المشروع</Button>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent closeLabel="إغلاق">
          <DialogHeader>
            <DialogTitle>حذف هذا المشروع؟</DialogTitle>
            <DialogDescription>ستُحذف المهام والملفات. لا يمكن التراجع.</DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <DialogClose render={<Button variant="ghost" />}>إبقاء المشروع</DialogClose>
            <DialogClose render={<Button variant="danger" />}>حذف</DialogClose>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}
```

## Accessibility

Provided by Base UI Dialog: `role="dialog"`, modal, focus moves into the popup on open, is trapped, and
returns to the trigger on close. Content behind it is made inert.

| Key | Action |
| --- | --- |
| `Esc` | Closes the dialog. |
| `Tab` / `Shift+Tab` | Cycles focus inside the dialog. |
| `Enter` / `Space` | Activates the focused button (trigger, close, footer actions). |

- Always render a `DialogTitle` so the dialog has an accessible name; add a `DialogDescription` for context.
- The × button has `aria-label={closeLabel}`. The default follows the Nasaq locale ("Close" / "إغلاق"; English without a `NasaqProvider`).

## RTL & i18n

- The × button and header use logical properties (`end-3`, `pe-8`, `text-start`), so they flip in RTL.
- The footer row aligns to the end via `sm:justify-end`, which follows `dir`.
- The only built-in string is the default `closeLabel`: "Close" in English, "إغلاق" when the Nasaq locale starts with `ar`.

## Styling & tokens

- Surface level 3: `bg-popover`, `text-popover-foreground`, `border-border`, `rounded-floating`.
- Backdrop: `bg-nq-fg/15` (light), `bg-nq-bg/60` (dark).
- Motion: opacity only, 150ms (`data-starting-style` / `data-ending-style`). The dialog does not move.
- Target `[data-slot=dialog-content]`, `[data-slot=dialog-backdrop]`, `[data-slot=dialog-close]`.

## Do / Don't

- **Do** put the confirming action last in the footer; use `variant="danger"` for destructive ones and name what is destroyed.
- **Do** keep it short; move long forms to a [`Sheet`](https://docs.nasaqui.com/components/sheet).
- **Don't** nest dialogs.
- **Don't** use a dialog for success or error messages; use [`toast`](https://docs.nasaqui.com/components/toast).

## Related

- [Sheet](https://docs.nasaqui.com/components/sheet) · [Button](https://docs.nasaqui.com/components/button) · [Toast](https://docs.nasaqui.com/components/toast)

## Lab

https://docs.nasaqui.com/?path=/docs/components-overlays-dialog--docs

## Code

### React

```tsx
import {
  Button, Dialog, DialogClose, DialogContent, DialogDescription,
  DialogFooter, DialogHeader, DialogTitle, DialogTrigger,
} from "@fadymondy/nasaq/web";

export function DeleteProject() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="danger" />}>Delete project</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Delete this project?</DialogTitle>
          <DialogDescription>Issues, time entries and files are removed. This cannot be undone.</DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Keep project</DialogClose>
          <DialogClose render={<Button variant="danger" />}>Delete</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

### shadcn

```tsx
import { Button } from "@/components/ui/button";
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";

export function DeleteProject() {
  return (
    <Dialog>
      <DialogTrigger render={<Button variant="danger" />}>Delete project</DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Delete this project?</DialogTitle>
          <DialogDescription>Issues, time entries and files are removed. This cannot be undone.</DialogDescription>
        </DialogHeader>
        <DialogFooter>
          <DialogClose render={<Button variant="ghost" />}>Keep project</DialogClose>
          <DialogClose render={<Button variant="danger" />}>Delete</DialogClose>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import {
  NqButton, NqDialog, NqDialogClose, NqDialogContent, NqDialogDescription,
  NqDialogFooter, NqDialogHeader, NqDialogTitle, NqDialogTrigger,
} from "@fadymondy/nasaq/vue";
</script>

<template>
  <NqDialog>
    <NqDialogTrigger as-child><NqButton variant="danger">Delete project</NqButton></NqDialogTrigger>
    <NqDialogContent>
      <NqDialogHeader>
        <NqDialogTitle>Delete this project?</NqDialogTitle>
        <NqDialogDescription>Issues, time entries and files are removed. This cannot be undone.</NqDialogDescription>
      </NqDialogHeader>
      <NqDialogFooter>
        <NqDialogClose as-child><NqButton variant="ghost">Keep project</NqButton></NqDialogClose>
        <NqDialogClose as-child><NqButton variant="danger">Delete</NqButton></NqDialogClose>
      </NqDialogFooter>
    </NqDialogContent>
  </NqDialog>
</template>
```

### Blade

```blade
<x-nq::dialog>
    <x-nq::dialog.trigger variant="danger">Delete project</x-nq::dialog.trigger>
    <x-nq::dialog.content>
        <x-nq::dialog.header>
            <x-nq::dialog.title>Delete this project?</x-nq::dialog.title>
            <x-nq::dialog.description>Issues, time entries and files are removed. This cannot be undone.</x-nq::dialog.description>
        </x-nq::dialog.header>
        <x-nq::dialog.footer>
            <x-nq::dialog.close variant="ghost">Keep project</x-nq::dialog.close>
            <x-nq::dialog.close variant="danger">Delete</x-nq::dialog.close>
        </x-nq::dialog.footer>
    </x-nq::dialog.content>
</x-nq::dialog>
```

### HTML + Alpine

```html
<div data-slot="dialog" x-data="nqDialog(false)" x-modelable="open" x-id="['nq-dialog']" class="contents">
    <button data-slot="dialog-trigger"
     type="button"                         aria-haspopup="dialog" x-on:click="show()" :aria-expanded="open" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-destructive text-destructive-foreground hover:bg-[color-mix(in_oklab,var(--nq-danger-solid)_88%,var(--nq-fg))] h-control px-[var(--nq-control-pad)]">
        Delete project</button>
    <template x-teleport="body">
    <div data-slot="dialog-portal">
        <div data-slot="dialog-backdrop" x-nq-presence="open" x-on:click="close()" class="fixed inset-0 z-50 bg-nq-fg/15 dark:bg-nq-bg/60 transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0"></div>
        <div data-slot="dialog-content" x-bind="popup" x-nq-presence="open" x-trap.noscroll="open"
            class="fixed inset-0 z-50 m-auto grid h-fit w-[calc(100%-2rem)] max-w-lg gap-4 rounded-floating border border-border bg-popover p-6 text-popover-foreground outline-none max-h-[calc(100dvh-2rem)] overflow-y-auto transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
            <div data-slot="dialog-header" class="flex flex-col gap-1.5 pe-8 text-start"><h2 data-slot="dialog-title" :id="$id('nq-dialog', 'title')" class="text-h3 text-foreground">Delete this project?</h2>
            <p data-slot="dialog-description" :id="$id('nq-dialog', 'description')" class="text-body-sm text-muted-foreground">Issues, time entries and files are removed. This cannot be undone.</p></div>
        <div data-slot="dialog-footer" class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end"><button data-slot="dialog-close"
     type="button"                         x-on:click="close()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 text-foreground hover:bg-nq-hover h-control px-[var(--nq-control-pad)]">
        Keep project</button>
            <button data-slot="dialog-close"
     type="button"                         x-on:click="close()" class="inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-control border border-transparent font-sans text-label transition-colors duration-150 ease-nq min-h-[var(--nq-touch-min,0px)] outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus disabled:pointer-events-none disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-destructive text-destructive-foreground hover:bg-[color-mix(in_oklab,var(--nq-danger-solid)_88%,var(--nq-fg))] h-control px-[var(--nq-control-pad)]">
        Delete</button></div>
                            <button type="button" data-slot="dialog-close" x-on:click="close()" aria-label="Close"
                    class="absolute end-3 top-3 inline-flex size-8 items-center justify-center rounded-control text-muted-foreground transition-colors duration-150 hover:bg-nq-hover hover:text-foreground focus-visible:outline-2 focus-visible:outline-nq-focus [&_svg]:size-4"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M18 6 6 18"/>
  <path d="m6 6 12 12"/>
</svg></button>
                    </div>
    </div>
</template>
</div>
```
