Patterns
Dialogs
Four components can interrupt the user, and choosing between them is the whole pattern. This page answers two questions: should this be a dialog at all, and if so, which one.
Figma source: Dialogs ★ ↗What a dialog is
A dialog is a conversation the interface starts. Everything in this pattern follows from taking that literally — the Figma anatomy is built on it, and each part maps to one side of the exchange.
| Part | The Figma definition | In practice |
|---|---|---|
| Header | “Explaining the dialog’s purpose to the user — what the purpose of a dialog is to the user.” | A title naming the task or the decision. Not “Alert”. |
| Content | “The conversation — what or how can the user interact; what the user needs to accomplish in order to complete the user’s task.” | The fields, the explanation, the consequence. |
| Actions | “Actionable buttons or dismissible actions — what the user interacts with to complete the user’s task.” | The confirming action, the way out, and nothing else. |
The file notes it directly: “On the Banner Alert, the message acts as a header, but also as the content, as the conversation with the user is one way.”
A banner tells; it does not ask. That is what makes it the only one of the four that does not interrupt — and the one to reach for whenever the user does not need to answer.
When should I use a dialog?
Before choosing between the four, decide whether to interrupt at all. A dialog takes the page away from the user, so it has to earn that.
When to use it
- The user must know something before they can safely continue.
- A short, self-contained task genuinely belongs in the flow of the current view.
- An action is about to happen that cannot be undone.
- Something failed and the user’s work is at risk.
When not to use it
- The content is long, multi-step, or has its own sub-navigation — that is a page.
- The information is useful but not urgent — use an inline Banner Alert.
- The user is likely to need the context behind it — use a Drawer.
- You are confirming something reversible — just do it, and offer undo.
Which dialog should I use?
The Figma page poses three questions. They resolve, in order, to exactly one component.
| Question from the file | If yes | If no |
|---|---|---|
| Does the user need to be alerted to anything? — and nothing more | Banner Alert. One-way, no lightbox, does not interrupt. | Continue ↓ |
| Can the user do anything else while this dialog is open? | Drawer, non-modal. The page behind stays live. | Continue ↓ |
| Should the user be able to complete multiple or complex interactions? | Basic Modal. Slotted content, focus trapped. | Confirmation Alert. One decision, two buttons. |
| Component | Modal? | Lightbox? | Interrupts? | Use for |
|---|---|---|---|---|
| Banner Alert | No | No | No | Telling the user something happened |
| Drawer | Optional | Optional | No | Supporting detail beside the main view |
| Basic Modal | Yes | Yes | Yes | A short self-contained task |
| Confirmation Alert | Yes | Yes | Yes | One irreversible decision |
Modal, non-modal, and the lightbox
Three properties get conflated constantly. They are independent, and naming them separately is what makes the pattern usable.
| Property | What it means | How it is implemented |
|---|---|---|
| Modal | The user cannot interact with the page behind it until they deal with the dialog. | aria-modal="true", focus trapped, background inert. |
| Non-modal | The dialog is open, and the page behind still works. | role="dialog" with no aria-modal, no focus trap. |
| Lightbox | The dimmed overlay. A visual signal only. | Primary Black at 25% — rgb(17 17 17 / 25%). |
The Figma note explains why it exists: “Lightbox can help draw attention to the fact that user cannot interact with page below the dialog.” Help draw attention — it is the visual half of a behaviour that must also exist in code.
Two failure modes follow. A lightbox without a focus trap lies: the page looks unavailable but Tab walks straight into it, which is disorienting for keyboard users and invisible to sighted mouse users. A focus trap without a lightbox is equally wrong: the page looks available and silently is not.
Modal and lightbox travel together. If you are adding one, add both.
The bad example, and why
One artboard is labelled “BAD EXAMPLE!! Full page coverage, incorrect tabindex — SHOULD JUST BE NEW PAGE.” Another records a real instance from the products: a Haplo required-field error surfaced as a browser prompt.
| Anti-pattern | Why it fails | Do this instead |
|---|---|---|
| A dialog covering the whole viewport | Nothing is left behind it to return to, so the lightbox and focus trap accomplish nothing — while the back button, the URL and the page title all stop describing where the user is. | Make it a page with its own URL, heading and breadcrumb. |
| Incorrect tabindex order | Focus escapes to the covered page, so a keyboard user is editing something they cannot see. | Trap focus inside the dialog; make the background inert. |
| A native browser prompt for validation | It cannot be styled, cannot be localised consistently, blocks the whole tab, and gives no route to the field at fault. | Inline field errors plus an error Banner Alert — see the Inputs pattern. |
| A dialog opened from a dialog | The user cannot tell what dismissing will return them to, and focus restoration becomes ambiguous. | Replace the content of the first dialog, or make the second step a page. |
Implementation checklist
Every modal dialog in the suite should satisfy all of these. They are behavioural, so they cannot be checked from a screenshot.
- Focus moves into the dialog on open — to the first field, or to the heading.
- Focus is trapped: Tab and Shift+Tab cycle within, and never reach the page behind.
- Escape closes it — and for a Confirmation Alert, Escape cancels, never confirms.
- Focus returns to the element that opened it on close.
- The dialog is labelled by its own heading via
aria-labelledby. - A Confirmation Alert uses
role="alertdialog"witharia-describedbypointing at the consequence. - Content behind is inert, not merely covered.
- Dismissing never discards unsaved input without warning.
- The lightbox is present wherever the dialog is modal.