MobTranslate styleguide

Alert Dialog

Interrupt only for a meaningful consequential choice with clear consequences, a safe default and an enabled return.

Use a consequential confirmation

Use AlertDialog for a brief important message requiring a response. Do not confirm every harmless toggle or use repeated confirmation as a substitute for understandable actions and undo. Use a regular Dialog for editing and an inline Alert for contextual guidance.

Describe the exact object, consequence and recovery. A button labelled Confirm is usually less useful than the actual action. In the local lab, removal affects no database, source or recording; a persistent restore control remains available.

Anatomy and specifications

PartContract
PopupNamed alertdialog with connected description; always modal
BackdropFixed 50% black dimming; outside-pointer dismissal blocked by the primitive
Surface24px padding; 2px border; --radius-lg; --shadow-xl; internal scroll
Sizesm 24rem / md 32rem / lg 42rem; md default; viewport minus 2rem width
FocusLeast destructive meaningful action first; a result that disables the trigger needs an alternate final focus
ActionsExplicit safe cancel and clearly named consequence; wrap at narrow widths

Working confirmation and independent restore

Local example draft is present.

tsx
'use client';
import { useRef, useState } from 'react';
import { AlertDialog, AlertDialogBackdrop, AlertDialogClose, AlertDialogDescription, AlertDialogPopup, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Button } from '@mobtranslate/ui';

export function ConfirmationContainmentExample() {
  const [open, setOpen] = useState(false);
  const [exists, setExists] = useState(true);
  const [result, setResult] = useState('Local example draft is present.');
  const cancel = useRef<HTMLButtonElement>(null);
  const restore = useRef<HTMLElement>(null);
  return <div data-confirmation-lab className="space-y-4">
    <AlertDialog open={open} onOpenChange={setOpen}>
      <AlertDialogTrigger disabled={!exists} render={<Button type="button" variant="destructive" />}>Remove local example</AlertDialogTrigger>
      <AlertDialogPortal><AlertDialogBackdrop /><AlertDialogPopup initialFocus={cancel} finalFocus={() => exists ? true : restore.current} className={state => state.open ? 'local-confirmation-open' : ''}>
        <AlertDialogTitle>Remove this local example?</AlertDialogTitle><AlertDialogDescription>This changes only the example on this page. A separate restore action remains available. No source, recording or database row will be deleted.</AlertDialogDescription>
        <div className="flex flex-wrap gap-3 mt-6"><AlertDialogClose ref={cancel} render={<Button type="button" variant="outline" />}>Keep local example</AlertDialogClose><Button type="button" variant="destructive" onClick={() => { setExists(false); setResult('Local example removed. Use Restore local example to recover.'); setOpen(false); }}>Confirm local removal</Button></div>
      </AlertDialogPopup></AlertDialogPortal>
    </AlertDialog>
    <Button ref={restore} type="button" variant="outline" aria-disabled={exists || undefined} onClick={() => { if (!exists) { setExists(true); setResult('Local example restored.'); } }}>Restore local example</Button>
    <p role="status">{result}</p>
  </div>;
}

Root API

Root propInstalled contract
open / defaultOpenControlled boolean / initial uncontrolled boolean; default false.
onOpenChange(open, eventDetails); details.reason describes cause; details.cancel() can reject a change.
onOpenChangeCompleteCalled after open/close animations finish; distinct from a submitted task succeeding.
modalDialog/Drawer: true default; false or trap-focus alternatives. AlertDialog is always modal.
disablePointerDismissalDialog/Drawer: false default. AlertDialog does not expose this prop and prevents outside-pointer dismissal.
actionsRefImperative close/unmount lifecycle; do not hold invisible inert content indefinitely.
handle / triggerId / defaultTriggerIdInstalled primitive trigger association/payload mechanisms; imperative triggers need a meaningful final focus destination.
childrenParts or installed payload render function. Root does not render its own DOM node.

Parts API

Exports: AlertDialog, AlertDialogTrigger, AlertDialogClose, AlertDialogPortal, AlertDialogBackdrop, AlertDialogPopup, AlertDialogTitle, AlertDialogDescription and AlertDialogPopupProps. Popup adds size=sm|md|lg, md default. AlertDialogClose is a close control, not an automatic destructive action. There is no exported automatic confirmation/request primitive.

Part / propContract
TriggerInstalled button/render props, disabled, nativeButton, trigger payload/association; button ref. Use render={<Button type="button" />} to compose one control.
CloseInstalled button/render props and button ref; closes the surface, not an application request.
PortalMounts the surface outside local layout; preserve theme variables through its actual container. keepMounted retains the subtree, not an open state.
BackdropDiv/render props, className string or state callback, style and div ref; visible dimming belongs to modality.
PopupDiv/render props and div ref; initialFocus and finalFocus accept boolean, element ref or interaction-type callback.
TitleInstalled heading/render props and heading ref; supplies the visible dialog name.
DescriptionInstalled paragraph/render props and paragraph ref; supplies a concise purpose/message.
className / render / native propsStyled parts retain primitive state callbacks. Custom render must preserve forwarded props, semantics and refs.

Focus, keyboard and dismissal

  • Choose initial focus deliberately: a labelled field for a small edit, a focusable heading for long structured content, or the least destructive action for a consequential choice.
  • Modal Tab/Shift+Tab remain inside; Escape closes when application policy permits. Always include a visible named close or cancel action.
  • Return to the invoking control, or a meaningful enabled alternative when that control is removed or disabled by the result.
  • Do not make long semantic content one flattened description. Use a concise description and retain headings, lists and paragraphs for normal reading.
  • A pending operation can prevent accidental dismissal only when a real cancellation/recovery path remains. Closing the surface is not cancelling a network request.
  • Non-modal use needs an explicit workflow and keyboard route between the surface and page. Do not claim modal blocking for modal=false or pointer blocking for trap-focus. Styled popup wrappers expose aria-modal only for fully modal roots; omit a dimming backdrop for non-modal/focus-only layouts.

Consequences, cancellation and failures

  • Cancel, Escape or merely opening the confirmation must not perform its consequential action. Outside-pointer dismissal is disabled for this primitive.
  • After local removal, the old trigger is disabled and finalFocus points to the enabled restore action. Keeping focus on a disabled or removed trigger is a failure.
  • For irreversible real work, choose the least destructive initial action and explain why recovery is unavailable. Never claim that a dialog itself supplies authorization.
  • If confirmation starts an asynchronous request, retain pending/failure/retry state and reconcile the actual persisted outcome before announcing success.
  • Avoid duplicate or generic action names and urgent dialogs for routine transient feedback.

Verification and release scope

  • Verify the actual title, description relationships, focus destination, keyboard operation and task result in the consuming application.
  • Exercise narrow and short viewports, both themes, long labels, text spacing, reduced motion and forced colors. Reach the last action by scrolling and keyboard.
  • For overlays, check background interaction, scroll locking and each permitted dismissal path. A role or aria-modal attribute alone does not prove modal behavior.
  • Use a named screen reader and browser for spoken output. Local examples and automated DOM checks do not prove announcements or a persisted backend result.
  • Retain source/review/permission evidence. A container or confirmation cannot grant authority or establish that a request succeeded.