Confirming destructive actions
A destructive action removes something the user cannot easily get back. Pick the lightest safeguard that fits the risk.
Confirm or undo?
Section titled “Confirm or undo?”| Situation | Use |
|---|---|
| The action is permanent, or affects other people or other data | Confirm with holo-dialog |
| The action is large, rare or expensive to repeat | Confirm with holo-dialog |
| The action is frequent, and you can restore the item | Do it at once, then offer undo in holo-toast |
Do not confirm everything. Constant dialogs teach people to click through them, which makes the important ones useless. If you can reverse the action, undo is faster and safer than a prompt.
Confirm with a dialog
Section titled “Confirm with a dialog”Put the consequence in the dialog, name what is affected, and make the danger button say what it does.
This permanently deletes the project and its 12 files. You cannot undo this.
<holo-button id="trigger" label="Delete project" variant="danger"></holo-button>
<holo-dialog id="dialog" heading="Delete Project Alpha?"> <p>This permanently deletes the project and its 12 files. You cannot undo this.</p> <holo-button slot="footer" id="cancel" label="Keep project" variant="secondary"></holo-button> <holo-button slot="footer" id="delete" label="Delete project" variant="danger"></holo-button></holo-dialog>const trigger = document.querySelector('#trigger');const dialog = document.querySelector('#dialog');
trigger.addEventListener('click', () => (dialog.open = true));document.querySelector('#cancel').addEventListener('click', () => (dialog.open = false));document.querySelector('#delete').addEventListener('click', async () => { dialog.open = false; await deleteProject(); // your code});Write clear consequences
Section titled “Write clear consequences”- Title the dialog with the verb and the object: “Delete Project Alpha?”, not “Are you sure?”.
- Say what is lost and whether it can be recovered. Give numbers where you have them.
- Label the buttons with actions, not “OK” and “Cancel”. “Delete project” and “Keep project” read correctly out of context, for example in a screen reader’s button list.
- Use
variant="danger"for the destructive button only. Make the safe choice the secondary button. - For irreversible, high-impact actions, ask the user to type the item’s name before the delete button works.
holo-dialog uses the native <dialog> element, so showModal() traps focus, Esc closes it, and the page behind becomes inert. When the dialog closes, it tries to return focus to the element that was focused when it opened.
holo-button keeps its real <button> inside a shadow root, so the host element may not accept focus. After the dialog closes, restore focus yourself to be certain:
dialog.addEventListener('holoClose', () => { trigger.shadowRoot?.querySelector('button')?.focus();});If the action removes the trigger (you deleted the item that held it), move focus somewhere sensible instead, such as the list heading or the next item. Never leave focus on <body>.
Do it, then offer undo
Section titled “Do it, then offer undo”For reversible actions, act immediately and show a toast with an undo button. Keep the toast visible long enough to use: several seconds, and it pauses while the pointer or focus is on it. Announce the result in words (“Project archived”), because holo-toast-region is a polite live region.
Project Alpha is active.
<holo-button id="archive" label="Archive project" variant="secondary"></holo-button> <holo-toast-region position="bottom-end"></holo-toast-region>const region = document.querySelector('holo-toast-region');
document.querySelector('#archive').addEventListener('click', async () => { archiveProject(); // your code: do it now
const toast = await region.addToast({ message: 'Project archived.', variant: 'info', duration: 8000 }); const undo = document.createElement('holo-button'); undo.label = 'Undo'; undo.variant = 'secondary'; undo.addEventListener('click', () => { restoreProject(); // your code: reverse it toast.remove(); }); toast.append(undo);});Only offer undo if you can really restore the item. If you delete data on the server straight away, an undo button is a lie. Either defer the delete until the toast expires, or confirm with a dialog instead.