Action sheet component
components specs/components/action-sheet.kmd
A context-anchored set of choices for the current task — slides up from the bottom on phones, presented as a popover on larger screens — used especially to confirm a destructive action with an explicit Cancel. Distinct from a dialog (which interrupts for an unrelated decision) and from a sheet (which holds content, not a choice list). Apple HIG parity (action sheets / activity-style confirmation).
When this spec applies
Primary triggers
- Build a bottom action sheet / destructive confirmation
All triggers
- Confirm a destructive action (Delete, Discard, Sign out)
- Offer a short list of context actions from a control
- Choose between dialog, menu, and action sheet for a choice
Specification body
Component — Action sheet
Status: v0.1.0 — Draft. Promoted from the Apple HIG parity scan (meta/docs/stack #080). Source: https://developer.apple.com/design/human-interface-guidelines/action-sheets
R1 — When to use (vs dialog / menu / sheet)
- Action sheet: 2–~5 mutually exclusive actions about the current context, anchored to the task — most importantly, confirming a destructive action. Originates from the user tapping a control.
- Dialog (
specs/components/dialogs.kmd): interrupts with an unrelated or system-initiated decision; centered/modal. - Menu (
specs/components/menus.kmd): many navigational/command items, non-destructive, anchored inline. - Sheet (
specs/components/sheets.kmd): holds content/forms, not a short action list.
If there is no destructive or context-bound choice, prefer a menu.
R2 — Anatomy
| Element | Required | Notes |
|---|---|---|
| Title | Optional | Short context ("Delete 3 files?") |
| Message | Optional | One line of consequence/clarification |
| Action buttons | Yes (1..~5) | Verb-labeled; one MAY be destructive (R3) |
| Cancel | Yes | Visually separated from the actions; dismisses with no effect |
R3 — Destructive action styling
- A destructive action uses the danger semantic token (red text/fill per
specs/themes/verge.kmd) and a clear verb ("Delete", not "OK"). - The destructive action is never the default/auto-focused control; Cancel is the safe default.
- At most one destructive action per sheet.
R4 — Placement by form factor
- Compact / phone: slides up anchored to the bottom safe-area
(
specs/app-layout/safe-area.kmd); a scrim dims the content; tapping the scrim = Cancel. - Regular / tablet-desktop: presented as a popover anchored to the triggering control (no full-width bottom bar). The choice set is identical; only the presentation adapts.
R5 — Dismissal
- Cancel, scrim tap, Esc (desktop), and the system back gesture all dismiss with no side effect.
- A destructive action requires a deliberate tap; it is never triggered by dismissal.
R6 — Accessibility & i18n
- Focus moves into the sheet on open; focus is trapped until dismissal; on close it returns to the trigger.
- The destructive action is announced as such (its label carries the meaning; color is not the only signal — WCAG 1.4.1).
- Reachable and operable by keyboard and screen reader; Cancel is always reachable.
- All labels translatable per
specs/i18n/contract.kmd; destructive verbs stay specific across locales.
Não-escopo
- Share/activity sheets (OS-provided share UI) — a platform integration.
- Content-bearing sheets (
specs/components/sheets.kmd). - Unrelated/system modals (
specs/components/dialogs.kmd).
Requirements (testable)
Requirement: Cancel is always present and separated {#req-action-sheet-cancel}
An action sheet SHALL always include a Cancel control that is visually separated from the action buttons and that dismisses the sheet with no side effect, per R2.
Scenario: Cancel rendered and separated
- GIVEN an action sheet with one or more action buttons
- WHEN the sheet is presented
- THEN a Cancel control is rendered
- AND it is placed in a visually separated group from the action buttons
Scenario: Cancel dismisses without effect
- GIVEN a presented action sheet anchored to a triggering control
- WHEN the user activates Cancel
- THEN the sheet is dismissed
- AND no action handler is invoked and no state mutation occurs
Requirement: Destructive action styling and safety {#req-action-sheet-destructive}
A destructive action SHALL use the danger semantic token with a specific verb label, SHALL NOT be the default or auto-focused control, and at most one destructive action SHALL appear per sheet, per R3.
Scenario: Destructive action uses danger token and specific verb
- GIVEN an action sheet declaring one action as destructive
- WHEN the sheet is presented
- THEN the destructive action renders with the danger semantic token from verge.kmd
- AND its label is a specific verb such as "Delete" rather than a generic "OK"
Scenario: Cancel is the safe default, not the destructive action
- GIVEN an action sheet containing a destructive action and a Cancel control
- WHEN the sheet opens
- THEN the destructive action is not the default or auto-focused control
- AND Cancel is the safe default
Scenario: At most one destructive action
- GIVEN a spec-conformant action sheet
- WHEN its action buttons are inspected
- THEN no more than one action is styled as destructive
Requirement: Placement adapts to form factor {#req-action-sheet-placement}
The action sheet SHALL slide up anchored to the bottom safe-area with a dimming scrim on compact form factors, and SHALL present as a popover anchored to the triggering control on regular form factors, while keeping an identical choice set, per R4.
Scenario: Compact presents as bottom sheet with scrim
- GIVEN a compact/phone form factor
- WHEN the action sheet is presented
- THEN it is anchored to the bottom safe-area and slides up
- AND a scrim dims the underlying content
Scenario: Regular presents as anchored popover
- GIVEN a regular/tablet-desktop form factor
- WHEN the action sheet is presented from a triggering control
- THEN it renders as a popover anchored to that control rather than a full-width bottom bar
- AND the set of actions offered is identical to the compact presentation
Requirement: Dismissal never triggers a destructive action {#req-action-sheet-dismissal}
Cancel, scrim tap, Esc, and the system back gesture SHALL all dismiss the sheet with no side effect, and a destructive action SHALL only run from a deliberate tap on it, never from dismissal, per R5.
Scenario: Scrim tap dismisses safely
- GIVEN a compact action sheet with a destructive action and a scrim
- WHEN the user taps the scrim
- THEN the sheet is dismissed with no side effect
- AND the destructive action is not triggered
Scenario: Esc and back gesture dismiss without destructive effect
- GIVEN a presented action sheet
- WHEN the user presses Esc on desktop or performs the system back gesture
- THEN the sheet is dismissed
- AND no destructive action handler is invoked
Requirement: Accessibility and keyboard obligation {#req-action-sheet-a11y}
On open the sheet SHALL move focus into itself and trap focus until dismissal, SHALL return focus to the trigger on close, SHALL keep Cancel reachable by keyboard and screen reader, and SHALL convey the destructive meaning through the label rather than color alone (WCAG 1.4.1), per R6.
Scenario: Focus enters, is trapped, and returns to trigger
- GIVEN a control that opens an action sheet
- WHEN the sheet opens
- THEN focus moves into the sheet and cannot leave it via keyboard until dismissal
- WHEN the sheet is dismissed
- THEN focus returns to the triggering control
Scenario: Destructive meaning not conveyed by color alone
- GIVEN an action sheet with a destructive action
- WHEN a screen reader inspects the destructive action
- THEN its accessible label carries the destructive meaning independent of color
- AND Cancel is reachable by keyboard and screen reader
References
specs/components/dialogs.kmdspecs/components/sheets.kmdspecs/components/menus.kmdspecs/components/buttons.kmdspecs/i18n/contract.kmd