AZMARA PLATFORM
Examples

Confirm-delete flow

A menu that opens a confirmation, then a toast with Undo, with the dialog kept next to the menu so it survives it closing.

View as Markdown for coding assistants, or see llms.txt.

Deleting is the flow that most needs care. This one asks, says what will be lost, does it and offers to take it back.

Preview theme
Preview density

Files

  • Quarterly report.pdf
  • Team photo.png
  • Brand guidelines.pdf
  • Budget 2027.xlsx
<div className="flex max-w-xl flex-col gap-3">
  {/* A label for the list, not a heading: a heading here would skip a level on the page. */}
  <p id="files-heading" className="m-0 text-base font-semibold">
    Files
  </p>
  {files.length === 0 ? (
    <EmptyState
      icon={<Icon icon={FolderOpen} size="xl" />}
      title="No files"
      description="Everything has been deleted. Undo from the notification, or reset the list."
      action={
        <Button size="sm" variant="outline" onClick={() => setFiles(INITIAL)}>
          Reset the list
        </Button>
      }
    />
  ) : (
    <ul
      ref={list}
      tabIndex={-1}
      aria-labelledby="files-heading"
      className="m-0 flex list-none flex-col divide-y divide-border rounded-lg border border-border p-0 outline-none focus-visible:ring-2 focus-visible:ring-ring"
    >
      {files.map((name) => (
        <li key={name} className="flex items-center justify-between gap-3 px-3 py-2 text-sm">
          <span className="flex items-center gap-2">
            <Icon icon={FileText} className="text-muted-foreground" />
            {name}
          </span>
          <DropdownMenu>
            <DropdownMenuTrigger
              render={
                <IconButton
                  icon={<Icon icon={MoreHorizontal} />}
                  label={`Actions for ${name}`}
                  variant="ghost"
                  size="sm"
                />
              }
            />
            <DropdownMenuContent>
              <DropdownMenuItem onClick={() => toast(`Downloading “${name}”`)}>
                Download
              </DropdownMenuItem>
              {/* The dialog is a sibling of the menu, so it outlives the menu closing. */}
              <DropdownMenuItem variant="destructive" onClick={() => setPending(name)}>
                Delete…
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        </li>
      ))}
    </ul>
  )}

  <AlertDialog open={pending !== null} onOpenChange={(open) => !open && setPending(null)}>
    <AlertDialogContent>
      <AlertDialogHeader>
        <AlertDialogTitle>Delete “{pending}”?</AlertDialogTitle>
        <AlertDialogDescription>
          It is removed for everyone with access. You can undo this for a few seconds
          afterwards.
        </AlertDialogDescription>
      </AlertDialogHeader>
      <AlertDialogFooter>
        {/* Focus starts on Cancel, so pressing Enter by reflex cannot delete anything. */}
        <AlertDialogCancel>Keep it</AlertDialogCancel>
        <AlertDialogAction onClick={() => pending && remove(pending)}>Delete</AlertDialogAction>
      </AlertDialogFooter>
    </AlertDialogContent>
  </AlertDialog>
</div>
Open as text

What it shows

  • A menu item that opens a dialog. The dialog is controlled and sits next to the menu, not inside it. Opening it from the item's onClick means it is still there when the menu closes. This is the same pattern as the recipe.
  • Safe focus. An alert dialog starts focus on its cancel button ("Keep it"), so pressing Enter by reflex cannot delete anything. Escape cancels.
  • Say what is being deleted. The title names the file, because "Delete this item?" is meaningless to a screen reader user who has lost track of which row they were on.
  • An undo that does not stack. The toast has an Undo action and a stable id, so deleting the same file twice replaces the toast. Undo puts the file back where it was.
  • Focus after the row is gone. The row that opened the menu no longer exists, so the example moves focus to the list. Without that, focus would fall back to the page.

Try it

Open the menu on a file with the keyboard (Tab to its button, then Enter), choose Delete…, confirm, and note where focus is. Then use Undo in the toast.

Limits

Undo works while the toast is visible. A real app should also delay the server call until then, or support restoring a deleted item, because a toast is easy to miss.