Skip to content
ShineUI
Install

ConfirmPopup

A small second thought.

<sui-confirm-popup>OverlaysStable

About

Small confirmation anchored to its trigger, for low-stakes destructive row actions such as deleting a draft. The trigger toggles a panel with the question and two buttons; outside clicks and Escape close it. Reach for it when a full modal would be heavier than the action deserves; use sui-confirm-dialog for anything that must be decided first.

Usage

<sui-confirm-popup message="Void this draft invoice?" confirm-label="Yes, void it" cancel-label="Keep"> <sui-button slot="trigger" variant="outline" label="Void draft"></sui-button> </sui-confirm-popup> <script type="module"> import { defineCustomElements } from "@shineui/core/loader"; import "@shineui/core/shineui.css"; defineCustomElements(); const el = document.querySelector("sui-confirm-popup"); el.addEventListener("suiClose", (e) => console.log(e.detail)); </script>
import { SuiConfirmPopup } from "@shineui/react"; import "@shineui/core/shineui.css"; export function Example() { return ( <SuiConfirmPopup message="Void this draft invoice?" confirmLabel="Yes, void it" cancelLabel="Keep" onSuiClose={(e) => console.log(e.detail)}> <sui-button slot="trigger" variant="outline" label="Void draft"></sui-button> </SuiConfirmPopup> ); }
<script setup> import { SuiConfirmPopup } from "@shineui/vue"; import "@shineui/core/shineui.css"; const onChange = (e) => console.log(e.detail); </script> <template> <SuiConfirmPopup message="Void this draft invoice?" confirm-label="Yes, void it" cancel-label="Keep" @sui-close="onChange"><sui-button slot="trigger" variant="outline" label="Void draft"></sui-button></SuiConfirmPopup> </template>
import { Component } from "@angular/core"; import { SuiConfirmPopup } from "@shineui/angular"; @Component({ standalone: true, imports: [SuiConfirmPopup], template: `<sui-confirm-popup message="Void this draft invoice?" confirm-label="Yes, void it" cancel-label="Keep" [(open)]="open" (suiClose)="onChange($event)"><sui-button slot="trigger" variant="outline" label="Void draft"></sui-button></sui-confirm-popup>`, }) export class Example { open = false; onChange(e: CustomEvent) { console.log(e.detail); } }

Basic

The panel is role="alertdialog" described by its message, and the trigger carries aria-haspopup="dialog", aria-controls and aria-expanded. Keep takes focus on open.

<div style="min-height:60px"> <sui-confirm-popup message="Void this draft invoice?" confirm-label="Yes, void it" cancel-label="Keep"> <sui-button slot="trigger" variant="outline" label="Void draft"></sui-button> </sui-confirm-popup> </div>

Open

open pins the panel for reading. It is not focus-trapped, so Tab carries on into the page; use sui-confirm-dialog where the decision must come first.

<div style="min-height:170px"> <sui-confirm-popup open message="Void this draft invoice?" confirm-label="Yes, void it" cancel-label="Keep"> <sui-button slot="trigger" variant="outline" label="Void draft"></sui-button> </sui-confirm-popup> </div>

Brand treatment

confirm-variant="brand" asks the same question with the brand button and a plain icon, for an action that is final but not destructive. danger is the default.

<div style="min-height:60px"> <sui-confirm-popup confirm-variant="brand" message="Send this statement now?" confirm-label="Send" cancel-label="Not yet"> <sui-button slot="trigger" variant="outline" label="Send statement"></sui-button> </sui-confirm-popup> </div>

Events

suiOpen fires when the trigger opens the panel, suiConfirm when the confirm button is pressed, and suiClose before a dismissal with the reason: cancel, escape, outside or trigger. position="left" opens the panel beside a right-edge action.

DR-1051 Silver Hills Church $1,200.00
DR-1052 Riverbend Dental Group $860.00
Two drafts.
<div id="rows" style="min-height:200px;font-size:13px"> <div class="row" data-row="DR-1051" style="display:flex;align-items:center;gap:12px;padding:9px 0;border-bottom:1px solid var(--hairline)"> <span style="flex:1">DR-1051 Silver Hills Church</span> <span style="font-feature-settings:'tnum'">$1,200.00</span> <sui-confirm-popup position="left" message="Delete this draft?" confirm-label="Delete" cancel-label="Keep"> <sui-icon-button slot="trigger" icon="fa-solid fa-trash" aria-label="Delete DR-1051" variant="ghost"></sui-icon-button> </sui-confirm-popup> </div> <div class="row" data-row="DR-1052" style="display:flex;align-items:center;gap:12px;padding:9px 0;border-bottom:1px solid var(--hairline)"> <span style="flex:1">DR-1052 Riverbend Dental Group</span> <span style="font-feature-settings:'tnum'">$860.00</span> <sui-confirm-popup position="left" message="Delete this draft?" confirm-label="Delete" cancel-label="Keep"> <sui-icon-button slot="trigger" icon="fa-solid fa-trash" aria-label="Delete DR-1052" variant="ghost"></sui-icon-button> </sui-confirm-popup> </div> <div id="rows-log" style="margin-top:10px;font-size:12px;color:var(--ink-muted)">Two drafts.</div> </div>
// setup(root): properties and events setup(root) { const log = root.querySelector('#rows-log'); root.querySelectorAll('.row').forEach((row) => { const popup = row.querySelector('sui-confirm-popup'); popup.addEventListener('suiOpen', () => { row.style.background = 'var(--surface-sunken)'; log.textContent = 'suiOpen on ' + row.dataset.row + '.'; }); popup.addEventListener('suiClose', (e) => { row.style.background = ''; log.textContent = 'suiClose (' + e.detail + ') on ' + row.dataset.row + '.'; }); popup.addEventListener('suiConfirm', () => { log.textContent = 'suiConfirm. Deleted ' + row.dataset.row + '.'; row.remove(); }); }); }

Keyboard

KeyAction
EscapeCloses the panel and returns focus to the trigger. Inside a dialog the panel takes the key first and the dialog stays.
Enter SpacePresses the focused button. Keep closes with reason cancel, the confirm button emits suiConfirm; both return focus to the trigger.
TabMoves from Keep to the confirm button. Past the confirm button it closes the panel with reason outside and carries on to the control after the trigger; Shift+Tab before Keep lands on the trigger. The panel is not focus-trapped.

API

<sui-confirm-popup>

Properties

PropertyAttributeTypeDefaultDescription
cancelLabelcancel-labelstringstring | undefinedLabel for the dismissing action.
confirmLabelconfirm-labelstringstring | undefinedVerb-first label for the confirming action.
confirmVariantconfirm-variant"brand" | "danger"'danger'The confirming button's look, which the warning icon follows: danger (the default, since a row action that asks first is usually a delete) is the danger button and the icon in the danger ink; brand asks the same question with the brand button and a plain icon, for an action that is final but not destructive. The same prop, with the same two values, as on sui-confirm-dialog.
messagemessagestringstring | undefinedThe question, one short line.
openopenbooleanfalseShows the panel (the reference defaultOpen). Mutable; the element opens and closes itself. On open, focus moves to the dismissing button. When the panel closes, a popup still open inside it closes with it and asks through its own suiClose with reason outside.
positionposition"bottom" | "left" | "right" | "top"'bottom'Preferred side of the trigger; the 248px panel sits 8px off it and flips to the opposite side when the viewport has no room. A row action at the right edge of a table wants left, a toolbar at the foot wants top.

Events

EventDetailReactVueDescription
suiCloseSuiCloseReason"cancel" | "close-button" | "escape" | "outside" | "pick" | "scrim" | "trigger"onSuiClose@sui-closeFires before the panel closes itself; detail is 'cancel' | 'escape' | 'outside' | 'trigger'. Cancel and Escape return focus to the trigger. Cancelable: preventDefault() keeps it open. It does not bubble, so a dialog around the trigger never hears it.
suiConfirmvoidonSuiConfirm@sui-confirmFires when the confirm button is pressed. Cancelable, as on sui-confirm-dialog: unless a listener calls preventDefault(), the panel closes itself and focus returns to the trigger.
suiOpenvoidonSuiOpen@sui-openFires when the trigger opens the panel. It does not bubble.

Slots

SlotDescription
(default)Rich message content; used alongside or instead of the message prop.
triggerThe row action that opens the confirmation (an icon button or button).

Types

SuiCloseReason typeWhy a dismissible surface is closing. Detail of the cancelable suiClose. A consumer expresses "a stray backdrop click must not close this" by calling preventDefault() on the reason it does not accept, which is why none of these needs a prop of its own.
No members.

Theming

Every design token this element can render with, read from its own stylesheets and the ones it imports. Set a token on :root or on any ancestor and every use below follows it. The values shown are the ones in the theme you are viewing. The list is read per stylesheet, so an element that imports a shared drawing module lists that module's tokens too, whether or not it paints with all of them.

See also