Dialogs

mandatory

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* Edgetoedge surface, app bar with close button 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) or rgba(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 error color
  • Title: question form ("Delete 12 messages?")
  • Body: consequences ("This cannot be undone.")
  • Actions: Cancel (text variant, default focus) + Delete (text or outlined w/ error color)
  • 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-labelledby pointing to title element id
  • aria-describedby pointing to body element id (optional but

    helpful)

  • 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 radiusxl (20 px) + ultralight blur scrim
glassmorphism Translucent surface + backdrop blur
brutalist Sharp corners + thick border + no shadow
terminal_classic Text-only with ASCII box drawing
  • foundations/ux-writing.kmd — title + body voice
  • navigation/back-behavior.kmd — back/ESC dismissal
  • themes/motion.kmd — entry/exit transitions
  • themes/elevation.kmd — level 5 default
  • errors/user-facing-messages.kmd — error dialog content

Source: ../home/koder/dev/koder/meta/docs/stack/specs/components/dialogs.kmd