Dialog
░░░░░ Overlay ░░░░░░░░░░░░░░░░░░░░░░░░░
░ ┌─── Dialog Container ───────────┐ ░
░ │ Title [Back] [✕] │ ░ ← Header
░ │───────────────────────────── │ ░
░ │ Body content │ ░ ← Body
░ │───────────────────────────── │ ░
░ │ [Cancel] [Confirm] │ ░ ← Fixed Footer
░ └────────────────────────────────┘ ░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
- Overlay — Full-screen semi-transparent background, color
--ds-color-special-mask, blocks underlying interactions
- Header — Title + optional back button + close button
- Body — Description text or form
- Fixed Footer — Fixed bottom bar supporting standard buttons or replacement-type footer
✅ Do
- Keep titles concise (e.g. "Confirm deletion" not "Are you sure you want to delete this item?")
- Use red confirm buttons for destructive actions (
destructive: true)
- Always provide a cancel/close path — keep at least one exit
- Keep forms inside dialogs simple (≤ 3 fields)
❌ Don't
- Don't nest dialogs — use a multi-step flow instead
- Don't show large amounts of content in a dialog — use a separate page
- Don't use dialogs for non-urgent information — use Toast instead
- Don't disable all closing paths — users need a way out
| State | Description |
|---|
| Open | Overlay fades in + dialog scales up from center; focus moves to first interactive element |
| Content interaction | Focus is trapped inside dialog (focus trap), Tab cycles within |
| Click overlay | Closes dialog (when closable=true) |
| Escape | Always available, closes dialog |
| Close | Animation reverses; focus returns to trigger element; page scroll resumes |
| Requirement | Implementation |
|---|
| Semantic role | role="dialog" + aria-modal="true" |
| Title association | aria-labelledby linked to the title element ID |
| Content description | aria-describedby linked to the body |
| Focus trap | Tab cycles within the dialog; cannot escape |
| Focus restoration | Focus returns to the trigger button on close |
| Scroll lock | Disables <body> scroll on open (overflow: hidden) |