# ConfirmPopover

> Asks "are you sure?" next to the control that triggered it: a non-modal alertdialog anchored to the trigger with a question, an optional consequence and Cancel / confirm buttons.

- Status: alpha
- Group: overlay
- Page: https://www.digitaltableteur.com/design-system/components/confirm-popover
- Contract: https://www.digitaltableteur.com/contracts/v1/ConfirmPopover.contract.json

## When to use

Asks "are you sure?" next to the control that triggered it: a non-modal alertdialog anchored to the trigger with a question, an optional consequence and Cancel / confirm buttons.

## When not to use

- Do not use it when the action can be undone; offer Undo instead.
- Do not put forms or long explanations inside; use Modal.

## Look-alikes

- Modal: Do not put forms or long explanations inside; use Modal.

## Props

| Prop | Type | Default | Description | Required |
| --- | --- | --- | --- | --- |
| `children` | `React.ReactElement` | none | The control that opens the confirmation, usually a Button. It keeps its own label and styling. | Yes |
| `title` | `string` | none | The question, for example "Clear the answers in this section?". Names the dialog. With choices, it asks which one, for example "What do you want to clear?". | Yes |
| `description` | `string` | none | What happens if the person confirms; describes the dialog. | No |
| `confirmLabel` | `string` | none | Label of the confirming button; repeat the action, for example "Clear answers". Not used with choices, where each choice has its own. | No |
| `choices` | `ConfirmPopoverChoice[]` | none | Several actions to pick from first; picking one asks "are you sure?" with that choice's question and labels. | No |
| `cancelLabel` | `string` | translated "Cancel" | Label of the cancelling button. | No |
| `tone` | `"error" \| "neutral"` | `neutral` | "error" for actions that remove or destroy something. | No |
| `onConfirm` | `(choiceId?: string) => void` | none | Called when the person confirms, with the picked choice's id when there are choices; the popover then closes. | Yes |
| `onOpenChange` | `(open: boolean) => void` | none | Called when the popover opens or closes, whichever way. | No |
| `side` | `"top" \| "right" \| "bottom" \| "left"` | `top` | Preferred side of the trigger; flips when out of room. | No |
| `align` | `"center" \| "start" \| "end"` | `start` | Alignment along that side. Not used with arrow, which always centres the popover on the trigger. | No |
| `arrow` | `boolean` | `false` | Draws a pointer from the middle of the popover's edge to the trigger, like a speech bubble; side picks the edge (top, right, bottom or left) and the popover centres on the trigger. Off by default. | No |
| `shortcut` | `string[]` | none | Keys that run the action without the popover, one entry per key; shown under the description as keycaps joined by plus signs. | No |
| `className` | `string` | none | Extra class on the popover surface. | No |

## Variants

- Available tones: `neutral`, `error` (default `neutral`).
- Allowed `side` values: `top`, `right`, `bottom`, `left` (default `top`).
- Allowed `align` values: `center`, `start`, `end` (default `start`).

## Invalid combinations

- SHOULD NOT: When `arrow` is set and `align` is one of `start`, `end`, then do not set `align`. A speech-bubble pointer sits in the middle of the popover's edge, so the popover centres on the trigger and align is ignored. Drop align, e.g. <ConfirmPopover side="left" arrow />.

## Keyboard

| Key | Behaviour |
| --- | --- |
| Enter / Space | On the trigger opens the popover. |
| General | Focus starts on Cancel, the safe choice; Tab moves between Cancel and confirm. |
| Escape | Cancels and returns focus to the trigger. |

## Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

| Level | Rule | How it is checked |
| --- | --- | --- |
| SHOULD NOT | When `arrow` is set and `align` is one of `start`, `end`, then do not set `align`. A speech-bubble pointer sits in the middle of the popover's edge, so the popover centres on the trigger and align is ignored. Drop align, e.g. <ConfirmPopover side="left" arrow />. | Checked by the MCP tool validate_component_usage (rule `confirmpopover-arrow-centres`). Also enforced in code: ConfirmPopover.tsx development warning (arrow && align !== "center"). |
| MUST | ARIA: The popover is role="alertdialog", named by `title` and described by `description`. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: The trigger exposes aria-expanded and aria-controls (Radix Popover). | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | ARIA: A click outside cancels; focus returns to the trigger. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by `npm run test:stories:matrix:ci`. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by `npm run test:stories:matrix:ci`. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by `npm run test:stories:hc`. |
| MUST | Every documented keyboard interaction works as specified. | Not yet verified: no evidence record. WCAG 2.1.1, 2.1.2. |

## Install and import

- In-repo only. ConfirmPopover is not exported from `@digitaltableteur/react`, so there is no public import path.
- Alpha components are internal and never exported.

## Links

- Machine-readable contract (Design System Contract 1.0): https://www.digitaltableteur.com/contracts/v1/ConfirmPopover.contract.json
- Markdown version of this page: https://www.digitaltableteur.com/design-system/components/confirm-popover.md
- Component index as JSON: https://www.digitaltableteur.com/design-system/components.json
- All components: https://www.digitaltableteur.com/design-system/components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.
