Skip to content
Alpha

Hololink UI is under active development. Components, APIs and these docs may change without notice.

Confirming destructive actions

A destructive action removes something the user cannot easily get back. Pick the lightest safeguard that fits the risk.

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.

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
});
  • 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>.

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.