Dialogs
Modal interruption surface — basic dialog (centered modal), full-screen dialog (mobile/onboarding), and alert dialog (destructive confirm). Material parity (`/components/dialogs`). Includes scrim, focus trap, ESC handling, and accessibility contract.
Spec — Dialogs
Facet *isual*do Koder Design. Material parity: https://m3.material.io/components/dialogs.
3 dialog variants
| Variant | Visual | Use |
|---|---|---|
| *asic* | Centered modal, max-width 560 px, surface bg, elevation 5 | Standard confirm / settings dialog |
| *ull-screen* | Edge |
Mobile multi-step flow, immersive picker |
| *lert* | Basic dialog with destructive icon + 2 buttons (Cancel + destructive) | Destructive confirmation |
Anatomy (basic)
┌──────────────────────────────────┐
│ [icon?] Title │ ← title row
│ │
│ Body content — explanation │ ← body
│ of the decision the user faces. │
│ │
│ [Cancel] [Action] │ ← actions, right-aligned
└──────────────────────────────────┘
Scrim overlay (page bg darkened)- *ontainer* radius-lg (16 px default), surface bg, elevation 5
- *adding* 24 px all sides
- *con*(optional): 24 px above title, accent color
- *itle*
headline-small(24/32, weight 600) - *ody*
body-medium(14/20) - *ctions* button row, right-aligned (or left in RTL), 8 px gap
R1 — Scrim
The semi-transparent overlay behind the dialog.
- Background:
rgba(0, 0, 0, 0.5)(light mode) orrgba(0, 0, 0, 0.7)(dark) - Click on scrim: dismisses dialog (unless
nonDismissible: true) - ESC key: dismisses dialog (unless
nonDismissible: true) - Click on scrim is equivalent to clicking "Cancel" action
Per interaction/states.kmd: animate scrim fade-in (medium duration, decelerate easing) and dialog scale 0.95→1.0 in parallel.
R2 — Full-screen variant
Used on Compact class (per window-size-classes.kmd) or for multi-step flows.
┌──────────────────────────────────┐
│ [X] Title [✓] │ ← app bar w/ close + confirm
├──────────────────────────────────┤
│ │
│ Content fills viewport │
│ │
│ │
└──────────────────────────────────┘- App bar at top with close button (left) + confirm action (right)
- Content fills below app bar
- No scrim (it IS the viewport)
- Back navigation per
navigation/back-behavior.kmd(back button = dismiss)
R3 — Alert dialog (destructive)
Basic dialog variant specifically for destructive confirms.
- Icon: warning or error glyph in
errorcolor - Title: question form ("Delete 12 messages?")
- Body: consequences ("This cannot be undone.")
- Actions: Cancel (text variant, default focus) + Delete (text or outlined w/
errorcolor) - Destructive action is RIGHT-aligned (Material convention; some
guidelines prefer left — Koder follows right)
Default focus: Cancel button (less destructive); Delete requires deliberate tab/click. Prevents accidental confirm on keyboard "Enter".
R4 — Focus trap
When dialog is open:
- Focus moves into the dialog (first focusable element OR explicitly
designated)
- Tab cycles within dialog only — can't tab to background
- Shift+Tab cycles backward within dialog
- On dismiss: focus returns to the element that opened the dialog
Per koder_kit (Flutter): Dialog widget handles this automatically. Per koder_web_kit: needs explicit focus trap implementation.
R5 — Lifecycle hooks
| Phase | Event |
|---|---|
| Pre-open | onWillOpen — can cancel by returning false |
| Open animation start | onOpening |
| Open animation end | onOpened — first focus applied |
| Action clicked | onAction(actionId) — return true to dismiss; false to keep open |
| Pre-dismiss | onWillClose — can cancel |
| Close animation start | onClosing |
| Close animation end | onClosed — focus returned to opener |
R6 — Title + body content rules
Per foundations/ux-writing.kmd:
- Title: question form for confirms, statement for info ("Saved.")
- Body: max 200 chars typically; explain the WHY or consequences
- Avoid jargon, avoid hedging ("might", "could possibly")
- Use direct voice ("Your changes are saved.")
R7 — Forbidden patterns
- ❌ Dialog with > 3 actions (use sheet/bottom-sheet instead)
- ❌ Nested dialogs (one at a time — second one replaces first)
- ❌ Auto-dismissing dialog after timer (use snackbar)
- ❌ Modal for non-critical info (use snackbar or tooltip)
- ❌ Dialog with form > 1 short input (use full-screen variant or dedicated page)
- ❌ Disabling close button as the ONLY exit (always provide Cancel/X)
R8 — Accessibility
role="dialog"+aria-modal="true"aria-labelledbypointing to title element idaria-describedbypointing to body element id (optional buthelpful)
- Title is
<h2>semantic - ESC key handler bound
- Focus visible on initial focus + on all interactive elements
- Screen reader announces title + body on open
R9 — Per-preset variation
| Preset | Treatment |
|---|---|
material3 |
Elevation 5 shadow + radius-lg |
windows_11 |
Mica backdrop instead of plain bg |
windows_95 |
3D bevel border + sharp corners + system title bar |
ios_cupertino |
radius |
glassmorphism |
Translucent surface + backdrop blur |
brutalist |
Sharp corners + thick border + no shadow |
terminal_classic |
Text-only with ASCII box drawing |
Cross-link
foundations/ux-writing.kmd— title + body voicenavigation/back-behavior.kmd— back/ESC dismissalthemes/motion.kmd— entry/exit transitionsthemes/elevation.kmd— level 5 defaulterrors/user-facing-messages.kmd— error dialog content