Dialog (Alert)
A native SwiftUI .alert modal that takes VoiceOver focus on presentation, with each action returning focus to the trigger because native alerts do not restore it automatically.
Selection
The criteria an agent checks before retrieving this component.
Use when
- Use when a brief, blocking message needs the user to acknowledge it or make a small choice before continuing (e.g., confirming a destructive action, acknowledging a required error). Uses the native SwiftUI
.alert()modifier. - Use when the choice is a short set of clearly labeled actions (typically one to three).
Try a different component when
- Do not use when the content is a non-blocking sheet of choices tied to a specific control (use
dialog.confirmation). - Do not use when the content is a larger form or multi-step flow (use
dialog.modal). - Do not use when the message is a transient status that does not require acknowledgement (use
global.announcements).
Must Haves
Non-negotiable structure. Every generated instance must satisfy these rules.
- Use the native
.alert(_:isPresented:actions:message:)modifier so the alert is a real modal that takes VoiceOver focus on presentation and blocks interaction with the rest of the screen until it is dismissed. - Provide the primary question or statement as the alert title, and any supporting detail in the
message:closure. - Return VoiceOver focus to the trigger on dismissal: bind the trigger with
@AccessibilityFocusStateand set it true inside every alert action's closure, because native alerts do not restore focus automatically (WCAG 2.4.3). - Give each action a specific label and the correct role:
.cancelfor the dismissive action and.destructivefor a destructive one, so VoiceOver and the system present them correctly. - Keep the action set short and the labels self-explanatory out of context (e.g., "Delete", "Cancel"), not "OK"/"Yes"/"No" where the outcome is ambiguous.
- Meets the touch target size baseline in
global_rules.md(global.touch-target-size). - Meets the system focus indicator baseline in
global_rules.md(global.focus-visible).
Donts
Avoid these accessibility and UX barriers.
- Do not build a custom view as a faux alert (a conditional
VStackoverlay); it does not receive VoiceOver focus on display, does not block the background, and does not restore focus on close. Use the native.alert(), ordialog.modalfor a richer custom modal. - Do not omit focus return; without
@AccessibilityFocusStateset in each action, VoiceOver focus is lost when the alert closes, which is a gap in the native control. - Do not rely on color alone to signal a destructive action; use the
.destructiverole and a clear label, not only red text. - Do not put lengthy content, forms, or many controls in an alert; use
dialog.modalfor that.
Customizable
Alternatives and options that give the AI agent some room to move.
- The alert may have a single acknowledgement button or two or more actions (e.g., Cancel plus a confirming or destructive action), as long as every action returns focus to the trigger.
- The confirming action may carry the
.destructiverole, the.confirmrole on OS versions that support it, or the default role when neither applies.
Golden Pattern
The tested reference implementation. Agents start from this shape and adapt to the developer’s codebase and context.
import SwiftUI
struct DialogAlertDemo: View {
@State private var showingAlert = false
@AccessibilityFocusState private var triggerFocused: Bool
var body: some View {
Button("Remove Download", role: .destructive) {
showingAlert = true
}
.accessibilityFocused($triggerFocused)
.alert("Remove this download?", isPresented: $showingAlert) {
// Each action returns VoiceOver focus to the trigger, since native
// alerts do not restore it automatically.
Button("Cancel", role: .cancel) {
triggerFocused = true
}
Button("Remove", role: .destructive) {
// perform the removal
triggerFocused = true
}
} message: {
Text("This episode will no longer be available offline.")
}
}
}
Acceptance Checks
The component’s test spec — an optional body of checks for verification.
Traits & semantics
- The alert is a modal presented over the screen; while it is open, only its content and actions are reachable.
- The native alert title is not exposed with the heading trait (an Apple gap); do not depend on a heading trait to convey the title.
VoiceOver
- When the alert opens, VoiceOver focus moves into it and the title and message are announced.
- Each action button speaks its specific label (e.g., "Cancel", "Delete").
- When any action dismisses the alert, VoiceOver focus returns to the trigger button.
Switch Control & Full Keyboard Access
- The alert's actions are reachable and activatable via Switch Control and a hardware keyboard, and dismissing returns focus to the trigger.
Dynamic Type
- The alert's title, message, and action labels scale with Dynamic Type and stay fully visible.
Visual
- Action labels are specific and do not depend on color alone. Native alert button text contrast can fall short (an Apple platform defect); verify against your target appearances.