Delete Confirmation Modal — Free HTML CSS JS Destructive Action Dialog Snippet

Delete Confirmation Modal · Modals · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Fade-and-scale entrance/exit animation via opacity and transform, not display toggling
Focus automatically moves to the dialog on open and returns to the trigger button on close
Escape key closes the modal via a listener attached only while the modal is actually open
Click-outside (backdrop) dismissal correctly distinguishes backdrop clicks from bubbled inner clicks
role="alertdialog", aria-modal, aria-labelledby, and aria-describedby for full screen reader support
Visually distinct destructive (red) vs. safe (neutral) button styling to reduce accidental confirmation
Warning icon and clear, specific "can't be undone" copy communicate consequence up front
confirmDelete() left as an obvious, clearly-commented integration point for real delete logic

About this UI Snippet

Delete Confirmation Modal — HTML, CSS & JavaScript Confirmation Dialog

Screenshot of the Delete Confirmation Modal snippet rendered live

Any destructive, irreversible action — deleting an account, a file, or an order — deserves a confirmation step that clearly explains the consequence and makes the destructive choice visually distinct from the safe one. This snippet builds that pattern: a centered dialog with a warning icon, a clear "can't be undone" message, and two buttons where Cancel is styled neutrally and Delete is styled in a warning red, so a rushed click is far more likely to land on the safe option.

How the open/close animation works

The overlay covers the full viewport with position: fixed; inset: 0 and starts at opacity: 0; pointer-events: none. Opening the modal adds an .open class, which fades the overlay in and simultaneously animates the dialog box itself from a slightly scaled-down, offset position (scale(0.92) translateY(8px)) up to its resting scale(1) translateY(0). Keeping the overlay in the DOM at all times (rather than conditionally rendering it) and toggling opacity/pointer-events is what makes this fade/scale entrance and exit possible — an element removed via display: none cannot transition.

How focus is managed for accessibility

openModal() stores document.activeElement (whatever had focus before the modal opened, typically the trigger button) in lastFocused, then immediately moves focus onto the confirm-delete button. This does two important things: it lets keyboard and screen reader users immediately know what action is available without tabbing, and — because the destructive button most people should still deliberately click rather than hit Enter reflexively — it puts real thought in the user's path since Enter on a freshly-focused destructive button is a common source of complaints if not handled carefully; many teams instead choose to focus Cancel by default for exactly this reason, which is a one-line change here. On closeModal(), focus is returned to lastFocused, so keyboard users land back exactly where they started instead of losing their place in the page.

How Escape and click-outside dismissal work

A keydown listener is attached to document only while the modal is open (added in openModal, removed in closeModal) and closes the modal on Escape. A separate click listener on the overlay checks e.target === overlay — this is what distinguishes a click on the dark backdrop itself from a click that merely bubbled up from inside the dialog box, since clicking inside the box would also technically "hit" the overlay via event bubbling if this check weren't in place.

Why the delete button is a placeholder

confirmDelete() is deliberately left as a simple stub with a comment marking where real API logic belongs — the pattern here (modal chrome, focus handling, keyboard/click dismissal) is meant to be reused regardless of what the actual destructive action does underneath.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Ask an AI assistant to explain the tradeoffs of focusing the Delete button by default versus focusing Cancel — this is a genuinely debated UX decision, and the assistant can walk through the reasoning for each and help you pick the safer default for your specific destructive action. It's also worth asking the assistant to add a full keyboard focus trap (intercepting Tab and Shift+Tab so focus cycles only within the dialog while it's open), which this snippet intentionally keeps simple by only handling initial focus and restoration.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "delete confirmation modal" in plain HTML, CSS, and vanilla JavaScript for confirming a destructive, irreversible action — no library, no framework.

Requirements:
- A trigger button that opens a centered modal dialog over a dimmed full-screen overlay, with a fade-in and slight scale-up entrance animation implemented via CSS transitions on opacity and transform (the overlay must remain in the DOM at all times, not be conditionally rendered).
- The dialog must use role="alertdialog", aria-modal="true", and aria-labelledby/aria-describedby pointing at its heading and description text.
- Two action buttons: a neutrally-styled Cancel button and a clearly red/destructive Delete button, visually distinct enough that a rushed click is unlikely to land on the wrong one.
- On open, move keyboard focus into the dialog. On close (by any method), return focus to whatever element had focus immediately before the dialog opened.
- The dialog must close on: clicking Cancel, clicking the dark backdrop specifically (not a click that merely bubbled up from inside the dialog box), and pressing the Escape key — with the Escape listener added only while the dialog is open and properly removed when it closes.
- Leave the actual delete action as an obviously-commented placeholder function so it is clear where real delete/API logic should be added.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Load the snippetClick "Delete Confirmation Modal" in the sidebar Library tab to load the trigger button and hidden modal.
  2. 2
    Open and inspectClick "Delete Item" in the preview and note the fade/scale entrance and that focus lands on the Delete button.
  3. 3
    Test dismissal methodsTry clicking Cancel, clicking the dark backdrop, and pressing Escape — all three should close the modal.
  4. 4
    Wire the real delete actionReplace the console.log inside confirmDelete() with your actual API call or state update, then call closeModal() after it succeeds.
  5. 5
    Customize the copyUpdate the heading and description text in the HTML panel to describe your specific item type and consequence.
  6. 6
    Export and saveExport as HTML/JSX/Tailwind or click "Save as" to reuse this modal pattern across your app's destructive actions.

Real-world uses

Common Use Cases

ADMIN
Account and data deletion flows
Confirm irreversible actions like account deletion, project removal, or permanent record deletion.
File and document management
Protect users from accidentally deleting files, folders, or attachments with a clear confirmation step.
SHOP
Cart and order management
Confirm removing an entire order or canceling a subscription, where the action has real financial consequence.
Admin and CMS content tools
Require explicit confirmation before deleting published content, users, or configuration in an admin panel.
Accessible modal dialog patterns
Study a complete example of focus trapping basics, ARIA dialog roles, and keyboard dismissal done correctly.
Learn overlay animation techniques
See how keeping a modal in the DOM and toggling opacity/transform enables smooth CSS-only entrance and exit animation.

Got questions?

Frequently Asked Questions

Keeping the overlay element present at all times and toggling an .open class lets the fade and scale transitions actually play. An element removed via display:none or conditional rendering has no state to animate from, so it would just appear and disappear instantly.

This snippet focuses the Delete button so keyboard users can act immediately, but many teams intentionally focus Cancel instead as a safety default, so an accidental Enter keypress does not trigger the destructive action. Both are valid choices — pick based on how catastrophic the specific delete action is.

The click listener is attached to the overlay element and checks that e.target is literally the overlay itself, not a descendant. A click inside the dialog box bubbles up through the overlay but its target is the inner element, so the check correctly ignores it.

The keydown listener is added to the document only inside openModal and explicitly removed inside closeModal, so it does not keep listening (or accumulate duplicate listeners) once the modal is no longer open.

Replace the console.log placeholder inside confirmDelete() with your actual API call or state update logic, and call closeModal() once that action completes successfully (or show an error state if it fails).

Yes — the dialog uses role="alertdialog" with aria-modal="true", and aria-labelledby/aria-describedby point to the heading and description text so screen readers announce the full context immediately when the dialog opens.

This snippet handles initial focus placement and focus restoration on close, but does not implement full Tab-key cycling containment. For a production app handling many different dialogs, consider a dedicated focus-trap utility to also intercept Tab and Shift+Tab within the dialog.