Skip to main content

Learning Track

Filter content by your role to see the most relevant sections

Dialogs & Overlay Patterns

Master the overlay family: dialogs that trap and return focus, sheets and popovers sized to intent, tooltips that respect delay and dismissal, entrance/exit motion from the interaction composites, and the elevation semantics that keep layering honest.

Darian Rosebrook
Staff Design Technologist, Design Systems Architect
Expertise:Design Systems, Overlays, Accessibility

Why This Matters

Overlays are the interface raising its voice. Done well, a dialog is a focused conversation—attention narrows, a decision happens, context resumes untouched. Done badly, it is an ambush: focus lost, background scrolling, the close button a mystery, the keyboard user stranded outside looking in. The difference is never visual polish; it is whether the overlay honors four contracts simultaneously—focus, dismissal, layering, and motion.

This system treats the whole family—Dialog, Sheet, Popover, Tooltip, Walkthrough—as one pattern with a shared contract, declared in each component's contract file and expressed through the foundations you have already met: elevation for layering, motion composites for entrance and exit, and the accessibility tree for everything that matters most.

Core Concepts: The Four Contracts

Contract 1: Focus Is Borrowed, Then Returned

An open overlay owns focus: it moves focus into itself on open, cycles Tab within itself while open (the trap), and—most forgotten—returns focus to the trigger on close. The Dialog contract declares exactly this:

// ui/components/Dialog/Dialog.contract.json (excerpt)
"role": "dialog",
"labeling": ["aria-label"],
"keyboard": [{ "key": "Enter|Space", /* trigger semantics */ },
              /* + Tab cycling, Escape to close */]

// The pattern's invariants:
//   open   → focus moves to the dialog (title or first control)
//   open   → Tab cycles inside; background is inert
//   close  → focus returns to the invoking trigger

The return is the invariant teams drop, and it is the one that strands keyboard users at the page start—their context destroyed by a closed dialog they successfully used.

Contract 2: Dismissal Is Obvious and Redundant

  • Escape closes—every overlay, no exceptions; it is the user's universal exit.
  • A visible close affordance—icon button with a real name ("Close"), the Icon binary's meaningful case.
  • Scrim click closes—except for destructive or flow dialogs, where an accidental click losing a form is worse than rigidity. The rule is declared per usage, and the destructive case says why it is exempt.

Contract 3: Layering Is Semantic, Not Decorative

Overlays sit at the top of the elevation story: the scrim is flat with the page, the dialog panel is surface.floating (level 2) or the modal step for the heavyweight cases, and the stacking order (elevation.depth) must agree with the shadow—a tooltip that renders above a modal in z-index but below it visually is a lie in one channel or the other. Nested overlays compose deliberately: popover over dialog over page, each layer's depth greater than its parent's.

Contract 4: Motion With an Exit Faster Than the Entrance

The composites encode the family's timing: modal enters on medium2 + soft.enter (333ms, comfortable arrival) and exits on medium1 + soft.exit (250ms)—arriving information is worth perceiving; departing information is not. Tooltips invert the ratio with a delay.medium (100ms) entry so grazing does not spam, and a short1 (50ms) exit so they vanish without ceremony. And the whole family carries its prefers-reduced-motion block: fades survive, travel does not.

Choosing the Family Member

Dialog    — interruptive, modal: the user must decide
Sheet     — the dialog's side-dwelling cousin (tasks, filters)
Popover   — anchored, transient, non-modal by default
Tooltip   — deferential: explains, never acts; no focus of its own
Walkthrough — sequenced guidance riding the same machinery

The chooser is attention budget, not aesthetics: how much of the
user's context does this surface need to take?

System Roles: Owning the Contracts

Design Impact

Designers own the attention decision—which family member, how modal, what the scrim obscures—and the destructive-dialog exemptions, argued per case.

Engineering Impact

Engineers own the invariants: trap, return, inert background, Escape, depth-order agreement. The contract file is where each is declared; the e2e walk is where each is proven.

Accessibility Impact

The a11y function owns the announcement quality: dialog names (aria-label carrying the title), the scrim's silence, and the manual pass on focus return.

Design & Code Interplay

The design side of overlays is the layer diagram: page, scrim, panel, nested layers—with the elevation role and the motion timing annotated per layer. The comp answers "how much context does this take?" visually (scrim opacity, panel offset) before code answers it structurally.

Applied Example: A Destructive Confirm Dialog

The hardest overlay used well—"Delete this project?":

  1. Family choice: interruption is the point → Dialog, modal, with a titled scrim.
  2. Wording: the title names the object; the body names the consequence; the buttons are "Delete project" / "Cancel" (the affirmative says what it does—no generic OK).
  3. Destructive styling: the affirmative action uses the destructive ramp, focus lands on Cancel (safe default first), and the scrim click is exempt—accidentally deleting is worse than one extra click.
  4. Contracts: Escape cancels; focus returns to the row's menu trigger; depth above any popover that opened the flow; motion on the composites with reduced-motion fades.
  5. Verify: the keyboard walk including focus return, axe on the open state, and one VoiceOver pass confirming the announced title.

Constraints & Trade-offs

  • Modality vs context: modality focuses decisions and blocks all else—the strongest tool and the most abused. The system bias: non-modal by default, modal by declared necessity.
  • Scrim dismissability vs safety: click-to-dismiss speeds casual flows and destroys careful ones; destructive dialogs opt out, loudly.
  • One overlay vs stacked overlays: stacks multiply every invariant; most stacks are a navigation design failure wearing a component solution.

Common Pitfalls & Failure Modes

1. The vanishing focus

Dialog closes, focus snaps to body. The return invariant exists precisely for this; it is contract behavior, not a nicety.

2. The leaky trap

Tab escapes to browser chrome or hidden background controls—background inertness is part of the same contract, not a separate feature.

3. Tooltip as control

Tooltips explain; they never hold actions—keyboard and touch users cannot reach them. Interactive content belongs in a popover with focus.

4. Depth disagreement

/* Bad: Level-3 shadow under a level-1 bar */
.modal { z-index: 10; box-shadow: var(--core-elevation-level-3); }
.nav   { z-index: 100; }
/* Good: Depth numbers order with shadow levels */

5. Motion without exit

Entrances animated, closes instant—the overlay family lives or exits together; the composites price both.

Verification Checklist

Additional Resources

  • Elevation & Shadows — the layering semantics overlays ride (/blueprints/foundations/elevation)
  • Motion & Duration — the composites that time the family (/blueprints/foundations/motion)
  • ARIA APG dialog pattern — the reference behavior (https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/)

Related Concepts

Reflection Questions