# Dialog

A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.

## Features

- Supports modal and non-modal modes.

- Focus is automatically trapped within modal.

- Can be controlled or uncontrolled.

- Manages screen reader announcements with `Title` and `Description` components.

- Esc closes the component automatically.

## Anatomy

Import all parts and piece them together.

```jsx
import { Dialog } from "@radix-solid/primitives";



export default () => (

	<Dialog.Root>

		<Dialog.Trigger />

		<Dialog.Portal>

			<Dialog.Overlay />

			<Dialog.Content>

				<Dialog.Title />

				<Dialog.Description />

				<Dialog.Close />

			</Dialog.Content>

		</Dialog.Portal>

	</Dialog.Root>

);
```

For a custom trigger, `asChild` takes a target factory, not an already-created JSX child. The target attaches the primitive's props, ref, and event composition to the selected host.

```tsx
<Dialog.Trigger asChild>

	{target => target("button", { class: "custom-trigger", children: "Open dialog" })}

</Dialog.Trigger>
```

## API Reference

### Root

Contains all the parts of a dialog.

| Prop                                                                                                                                                                 | Type       | Default |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- |
| `defaultOpen`The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.                                         | `boolean`  | —       |
| `open`The controlled open state of the dialog. Must be used in conjunction with `onOpenChange`.                                                                      | `boolean`  | —       |
| `onOpenChange`Event handler called when the open state of the dialog changes.                                                                                        | `function` | —       |
| `modal`The modality of the dialog. When set to `true`, interaction with outside elements will be disabled and only dialog content will be visible to screen readers. | `boolean`  | `true`  |

### Trigger

The button that opens the dialog.

| Prop                                                                                                                                                                                     | Type      | Default |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------- |
| `asChild`Render a different host with a target factory such as `target => target('button', { children: 'Open' })`. Read our [Composition](../guides/composition) guide for more details. | `boolean` | `false` |

| Data attribute | Values               |
| -------------- | -------------------- |
| `[data-state]` | `"open" \| "closed"` |

### Portal

When used, portals your overlay and content parts into the `body`.

| Prop                                                                                                                                                                                                                          | Type          | Default         |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------------- |
| `forceMount`Used to force mounting when more control is needed. Useful when controlling animation with native Solid animation libraries. If used on this part, it will be inherited by `Dialog.Overlay` and `Dialog.Content`. | `boolean`     | —               |
| `container`Specify a container element to portal the content into.                                                                                                                                                            | `HTMLElement` | `document.body` |

### Overlay

A layer that covers the inert portion of the view when the dialog is open.

| Prop                                                                                                                                                                                  | Type      | Default |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------- |
| `asChild`Render a different host with a target factory such as `target => target('div', { class: 'overlay' })`. Read our [Composition](../guides/composition) guide for more details. | `boolean` | `false` |
| `forceMount`Used to force mounting when more control is needed. Useful when controlling animation with native Solid animation libraries. It inherits from `Dialog.Portal`.            | `boolean` | —       |

| Data attribute | Values               |
| -------------- | -------------------- |
| `[data-state]` | `"open" \| "closed"` |

### Content

Contains content to be rendered in the open dialog.

| Prop                                                                                                                                                                                         | Type       | Default |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- |
| `asChild`Render a different host with a target factory such as `target => target('section', { children: 'Content' })`. Read our [Composition](../guides/composition) guide for more details. | `boolean`  | `false` |
| `forceMount`Used to force mounting when more control is needed. Useful when controlling animation with native Solid animation libraries. It inherits from `Dialog.Portal`.                   | `boolean`  | —       |
| `onOpenAutoFocus`Event handler called when focus moves into the component after opening. It can be prevented by calling `event.preventDefault`.                                              | `function` | —       |
| `onCloseAutoFocus`Event handler called when focus moves to the trigger after closing. It can be prevented by calling `event.preventDefault`.                                                 | `function` | —       |
| `onEscapeKeyDown`Event handler called when the escape key is down. It can be prevented by calling `event.preventDefault`.                                                                    | `function` | —       |
| `onPointerDownOutside`Event handler called when a pointer event occurs outside the bounds of the component. It can be prevented by calling `event.preventDefault`.                           | `function` | —       |
| `onInteractOutside`Event handler called when an interaction (pointer or focus event) happens outside the bounds of the component. It can be prevented by calling `event.preventDefault`.     | `function` | —       |

| Data attribute | Values               |
| -------------- | -------------------- |
| `[data-state]` | `"open" \| "closed"` |

### Close

The button that closes the dialog.

| Prop                                                                                                                                                                                      | Type      | Default |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------- |
| `asChild`Render a different host with a target factory such as `target => target('button', { children: 'Close' })`. Read our [Composition](../guides/composition) guide for more details. | `boolean` | `false` |

### Title

An accessible title to be announced when the dialog is opened.

If you want to hide the title, use our [Visually Hidden](../utilities/visually-hidden) utility: `<VisuallyHidden.Root asChild>{target => target(Dialog.Title, { children: "Title" })}</VisuallyHidden.Root>`.

| Prop                                                                                                                                                                                  | Type      | Default |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------- |
| `asChild`Render a different host with a target factory such as `target => target('h2', { children: 'Title' })`. Read our [Composition](../guides/composition) guide for more details. | `boolean` | `false` |

### Description

An optional accessible description to be announced when the dialog is opened.

If you want to hide the description, use our [Visually Hidden](../utilities/visually-hidden) utility: `<VisuallyHidden.Root asChild>{target => target(Dialog.Description, { children: "Description" })}</VisuallyHidden.Root>`. To remove the description entirely, omit this part and pass `aria-describedby={undefined}` to `Dialog.Content`.

| Prop                                                                                                                                                                                       | Type      | Default |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | ------- |
| `asChild`Render a different host with a target factory such as `target => target('p', { children: 'Description' })`. Read our [Composition](../guides/composition) guide for more details. | `boolean` | `false` |

## Examples

### Close after asynchronous form submission

Use the controlled props to programmatically close the Dialog after an async operation has completed.

```jsx
import { createSignal } from "solid-js";

import { Dialog } from "@radix-solid/primitives";



const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));



export default () => {

	const [open, setOpen] = createSignal(false);



	return (

		<Dialog.Root open={open()} onOpenChange={setOpen}>

			<Dialog.Trigger>Open</Dialog.Trigger>

			<Dialog.Portal>

				<Dialog.Overlay />

				<Dialog.Content>

					<form
						onSubmit={(event) => {
							wait().then(() => setOpen(false));
							event.preventDefault();
						}}
					>

						{/** some inputs */}

						<button type="submit">Submit</button>

					</form>

				</Dialog.Content>

			</Dialog.Portal>

		</Dialog.Root>

	);

};
```

### Scrollable overlay

Move the content inside the overlay to render a dialog with overflow.

```jsx
// index.tsx

import { Dialog } from "@radix-solid/primitives";

import "./styles.css";



export default () => {

	return (

		<Dialog.Root>

			<Dialog.Trigger />

			<Dialog.Portal>

				<Dialog.Overlay class="DialogOverlay">

					<Dialog.Content class="DialogContent">...</Dialog.Content>

				</Dialog.Overlay>

			</Dialog.Portal>

		</Dialog.Root>

	);

};
```

```css
/* styles.css */

.DialogOverlay {

	background: rgba(0 0 0 / 0.5);

	position: fixed;

	top: 0;

	left: 0;

	right: 0;

	bottom: 0;

	display: grid;

	place-items: center;

	overflow-y: auto;

}



.DialogContent {

	min-width: 300px;

	background: white;

	padding: 30px;

	border-radius: 4px;

}
```

### Custom portal container

Customise the element that your dialog portals into.

```tsx
import { createSignal } from "solid-js";

import { Dialog } from "@radix-solid/primitives";



export default () => {

	const [container, setContainer] = createSignal<HTMLDivElement | null>(null);

	return (

		<div>

			<Dialog.Root>

				<Dialog.Trigger />

				<Dialog.Portal container={container()}>

					<Dialog.Overlay />

					<Dialog.Content>...</Dialog.Content>

				</Dialog.Portal>

			</Dialog.Root>



			<div ref={node => { setContainer(node); }} />

		</div>

	);

};
```

## Accessibility

Adheres to the [Dialog WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal).

### Keyboard Interactions

| Key           | Description                                            |
| ------------- | ------------------------------------------------------ |
| `Space`       | Opens/closes the dialog.                               |
| `Enter`       | Opens/closes the dialog.                               |
| `Tab`         | Moves focus to the next focusable element.             |
| `Shift + Tab` | Moves focus to the previous focusable element.         |
| `Esc`         | Closes the dialog and moves focus to `Dialog.Trigger`. |

## Custom APIs

Create your own API by abstracting the primitive parts into your own component.

### Abstract the overlay and the close button

This example abstracts the `Dialog.Overlay` and `Dialog.Close` parts.

#### Usage

```jsx
import { Dialog, DialogTrigger, DialogContent } from "./your-dialog";



export default () => (

	<Dialog>

		<DialogTrigger>Dialog trigger</DialogTrigger>

		<DialogContent>Dialog Content</DialogContent>

	</Dialog>

);
```

#### Implementation

```tsx
// your-dialog.tsx

import { omit } from "solid-js";

import type { ComponentProps } from "@solidjs/web";

import { Dialog as DialogPrimitive } from "@radix-solid/primitives";

import { Cross1Icon } from "@radix-solid/icons";



export function DialogContent(props: Extract<ComponentProps<typeof DialogPrimitive.Content>, { asChild?: false }>) {

	return (

		<DialogPrimitive.Portal>

			<DialogPrimitive.Overlay />

			<DialogPrimitive.Content {...omit(props, "children")}>

				{props.children}

				<DialogPrimitive.Close aria-label="Close">

					<Cross1Icon />

				</DialogPrimitive.Close>

			</DialogPrimitive.Content>

		</DialogPrimitive.Portal>

	);

}



export const Dialog = DialogPrimitive.Root;

export const DialogTrigger = DialogPrimitive.Trigger;
```
