Compose components

Build an accessible Dialog

Compose a modal interaction with a real trigger, title, description, focus management, and explicit close actions.

Dialog is an example of why accessible primitives matter. A useful modal must manage focus, keyboard dismissal, labelling, and the surrounding page. A div with a fixed position is not enough.

Add the Dialog component and use the generated parts for its trigger, content, title, description, and close controls. Keep a visible title unless the accessible name is supplied another documented way. Test the primitive default outside-click and Escape behavior before overriding it. Changing dismissal can trap or surprise users.

Focus should enter the dialog at a useful control, remain within it while open, and return to the trigger on close. That trigger must still exist. Opening from an item that is deleted during the interaction needs an explicit fallback focus target. Destructive confirmation should identify what will be lost rather than use a vague “Are you sure?”

When a dialog contains a save operation, controlled open state is often necessary. Do not close on submit before the server succeeds, because a failed request would hide the error and entered data. Keep the content usable on a short mobile viewport and with the on-screen keyboard visible.

npx shadcn@latest add dialog

Wire the parts together with a real trigger and title:

<Dialog>
  <DialogTrigger asChild>
    <Button variant='outline'>Edit project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Edit project</DialogTitle>
      <DialogDescription>Update the name and description.</DialogDescription>
    </DialogHeader>
    {/* form fields */}
  </DialogContent>
</Dialog>

Open the dialog with the keyboard, tab through it, press Escape, and check that focus returns to “Edit project”. Tab should stay trapped inside the open dialog until you dismiss it.

Add a delayed failed save and verify the dialog stays open, values remain, the error is announced, and the content scrolls on a short viewport.

Lesson completed