Feedback/DSModal
DSModal
feedbackCentered overlay dialog with a header, scrollable content area, and footer actions. Blocks background interaction.
iOS 17+macOS 14+
Purpose
Confirmation dialogs, forms requiring immediate attention, and multi-step mini-flows.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| isPresentedreq | Binding<Bool> | — | Controls visibility. |
| titlereq | String | — | Modal heading. |
| size | DSModalSize | .md | Width constraint (.sm, .md, .lg, .full). |
| contentreq | ViewBuilder | — | Main modal content. |
| actions | ViewBuilder? | nil | Footer action buttons. |
Examples
Confirmation dialog
Delete confirmation before an irreversible action.
swift
DSModal(isPresented: $showDelete, title: "Delete project?") {
Text("This action cannot be undone.")
.font(.system(size: 15))
.foregroundStyle(DSColor.muted)
} actions: {
HStack {
DSButton("Cancel", variant: .ghost) { showDelete = false }
DSButton("Delete", variant: .destructive) { viewModel.delete() }
}
}Form modal
Multi-field form in a modal.
swift
DSModal(isPresented: $showAddTask, title: "New Task", size: .md) {
VStack(alignment: .leading, spacing: DSSpacing.md) {
DSTextField(label: "Title", text: $taskTitle)
DSTextArea(label: "Description", text: $taskDescription)
DSSelectField(label: "Priority", selection: $priority, options: priorities)
}
} actions: {
HStack {
DSButton("Cancel", variant: .ghost) { showAddTask = false }
DSButton("Create", variant: .primary, isLoading: viewModel.isSaving) {
viewModel.createTask()
}
}
}Composition: Multi-step flow
Modal with steps and navigation.
swift
DSModal(isPresented: $showOnboarding, title: "Welcome", size: .lg) {
VStack(spacing: DSSpacing.lg) {
if step == 1 {
OnboardingStep1()
} else if step == 2 {
OnboardingStep2()
} else {
OnboardingStep3()
}
}
} actions: {
HStack {
if step > 1 {
DSButton("Back", variant: .ghost) { step -= 1 }
}
Spacer()
DSButton(step == 3 ? "Finish" : "Next", variant: .primary) {
if step < 3 { step += 1 } else { completeOnboarding() }
}
}
}When to Use
✓ Use DSModal for:
- • Critical confirmations (delete, cancel subscription)
- • Forms requiring immediate attention
- • Multi-step flows that block the main UI
- • Content that needs full user focus
✗ Avoid using for:
- • Quick confirmations (use DSToast)
- • Non-blocking forms (use inline or DSBottomSheet)
- • Mobile-first actions (use DSBottomSheet on iOS)
- • Simple yes/no questions (use native .alert())
Consider instead:
DSBottomSheet
For iOS-native bottom sheets with better mobile UX
DSAlert
For inline, non-blocking alerts
DSToast
For quick confirmations that auto-dismiss
Accessibility
VoiceOver
Announces 'Modal, [title]'. Focus trapped within modal
Keyboard
- • Escape to dismiss
- • Tab cycles through interactive elements
- • Return to confirm primary action
Dynamic Type
✓ Supported
Contrast
WCAG AA compliant (overlay 3:1, content 4.5:1)
Traits
.isModalFocus trap activeBackground dimmed and non-interactive