Skip to content

Dialog

A dialog holds a decision or a short task that people finish before going back to the page. It dims the page behind it and returns focus to the control that opened it.

When to use it

Open a dialog when the page cannot go on without an answer, such as before deleting a candidate, or for a short form such as adding a task.

If the action can be undone, do it at once and offer Undo in a toast. For details read beside the page, use a side sheet, and for a few controls on one item, a popover.

Anatomy

The body scrolls when it runs taller than the dialog, while the header and footer stay put. A thin line shows under the header or above the footer only while content hides behind it.

Title
17 px on a 24 px line, weight 700
Description
14 px on a 22 px line, 4 px below the title
--type-small, --text-muted
Close button
32 px, 44 px on touch screens
Header padding
16 px top, 20 px left, 12 px right
--space-4, --space-5, --space-3
Body padding
8 px top, 20 px sides
--space-2, --space-5
Footer padding
16 px top and bottom, 20 px sides
--space-4, --space-5
Corner radius
16 px
--radius-2xl
Maximum height
92 percent of the window

Sizes

A dialog keeps at least 24 px from each side of the window. Below 640 px it opens as a bottom sheet, and a large one fills the screen.

Actions and their order

Actions sit at the right end of the footer: Cancel first, the completing action last. The completing action takes the primary button, or the danger button when it deletes.

When they do not fit on one line, they stack at full width with the completing action on top.

A confirmation opens with focus on Cancel, so an Enter pressed out of habit keeps the data.

Button height
36 px, 44 px on touch screens
Gap between actions
8 px
--space-2
Actions in one footer
at most three

Closing

Once something is typed, Escape, the close button and a click on the dimmed page first ask “Discard the changes?” Cancel never asks: its label already says what happens.

Focus then goes back to the control that opened the dialog, or, if the dialog removed it, to the next row, the previous one or the list.

Escape
closes it, and in a confirmation answers Cancel
Close button
closes it. A confirmation has none
Click on the dimmed page
closes it. A confirmation ignores it

Keyboard and focus

When a dialog opens, focus goes to the first field of a form, to Cancel in a confirmation, or to the dialog itself when it only shows content. The page behind stops scrolling and ignores the pointer, keys and screen readers.

Tab, Shift+Tab
next or previous control, wrapping at the ends
Enter in a text field
submits the form

Saving and errors

While the completing action waits for the server, its button is busy at once and shows a spinner after 300 ms. The rest of the dialog stays usable.

If a field is wrong, focus moves to the first field with an error. If saving fails, a message at the top of the body says so and the entries stay as typed.

While content loads, the header and footer show at once over a skeleton of the body.

Add task while it saves, and after a save failed.

Content

The title names the task or asks the question. The body adds only what the title cannot hold, starting with the consequence. Buttons answer the title with a verb, plus a noun when the verb alone could mean two things.

Permitted

  • Delete this candidate permanently?

    The question people answer.

  • Delete candidate

    It repeats the verb of the title.

Not permitted

  • Are you sure?

    It hides what is at stake.

  • OK

    It names no action.

Accessibility

A dialog has role dialog, a confirmation role alertdialog, both with aria-modal. The title labels it, and a confirmation's body describes it, so a screen reader reads the question with its consequence.

The close button is named Close.

NextSheet

Dialog

Stops the page for a decision or a short task and returns focus to the control that opened it.

  • Kind: component
  • Page: https://design.werklist.com/components/dialog
  • Version: 2.1.2

When to use it

  • A decision the page cannot go on without, such as deleting a candidate for good
  • A short form that belongs to one moment, such as adding a task
  • A record with its history, in the large size

When not to use it

  • An action that can be undone: act at once and offer Undo in a toast
  • Details read beside the page: use a side sheet
  • A few controls for one item: use a popover
  • Information that needs no answer: use a toast or an inline message
  • A dialog opened from a dialog, except a confirmation about the dialog beneath

Anatomy

  • Layer: covers the whole window, above the page (--layer-dialog)
  • Scrim: no blur, fades in over 150 ms (--overlay)
  • Margin to the window: 24 px (--space-6)
  • Panel: radius 16 px, at most 92 percent of the window high (--glass-fill-raised, --glass-blur-raised, --elevation-3, --radius-2xl)
  • Panel edge, dark theme: a 1 px top highlight and a 0.5 px ring over the shadow. The light theme shows the shadow alone (--glass-highlight, --glass-ring-raised)
  • Header: padding 16 px top, 20 px left, 12 px right (--space-4, --space-5, --space-3)
  • Title: h2, 17 px on a 24 px line, weight 700 (--text)
  • Description: 14 px on 22 px, 4 px below the title (--type-small, --text-muted)
  • Close button: icon button, 32 px, 44 px on touch screens, with a 16 px cross
  • Body: padding 8 px top, 20 px sides, 20 px bottom without a footer, 8 px with one. Scrolls on its own, and the page behind stays still (--space-2, --space-5, --type-small, --text)
  • Footer: padding 16 px top and bottom, 20 px sides. Actions at the end, 8 px apart (--space-4, --space-5, --space-2)
  • Footer buttons: 36 px high, 44 px on touch screens
  • Line while scrolled: 1 px under the header while content is hidden above, above the footer while content is hidden below (--border-rule)

Variants

  • Standard: A form or content. role dialog, close button, closes on a scrim click when nothing is typed
  • Confirmation: A question before an action that cannot be undone. role alertdialog, small size, no close button, the scrim does nothing, focus starts on Cancel, the verb takes the danger button when it deletes

Sizes

  • Small, 448 px wide: confirmations and notices
  • Medium, 512 px wide: forms
  • Large, 896 px wide: a record with its history
  • Below 640 px: a bottom sheet. Large fills the screen inside the safe areas

States

  • Opening: The scrim fades in over 150 ms. The panel fades in, grows from 97 percent and rises 8 px, over 200 ms (--duration-fast, --duration-base, --ease-soft)
  • Closing: Reverse, over 150 ms (--duration-fast, --ease-standard)
  • Reduced motion: Opacity only (--duration-fast)
  • Scrolled: The 1 px line shows under the header or above the footer while content is hidden behind it (--border-rule)
  • Busy: The completing button is busy at once (aria-busy, presses ignored) and shows its spinner after 300 ms. The rest of the dialog stays usable (--duration-slow)
  • Save failed: A message in the error tone at the top of the body, role alert. The entries stay as typed and focus stays on the button (--error-ink)
  • Invalid entry: After a failed submit, focus moves to the first field with an error, and its message sits under that field
  • Content loading: Header and footer show at once over a skeleton of the body
  • Unsaved entries: Escape, the close button, a scrim click and a swipe open the confirmation Discard the changes? with Keep editing and Discard. Cancel closes without asking

Keyboard

  • Tab: Next control. From the last back to the first
  • Shift+Tab: Previous control. From the first to the last
  • Enter in a single line field: Submits the form
  • Enter or Space on a button: Activates it
  • Escape: Closes the front layer only. Asks first when entries are unsaved. In a confirmation, answers Cancel

Accessibility

  • role dialog or alertdialog, aria-modal true, aria-labelledby the title. Aria-describedby the body of a confirmation
  • Focus on open: the first field of a form, Cancel in a confirmation, the panel itself (tabindex -1) when it only shows content
  • Tab reaches only the controls that show in the dialog
  • Focus on close: the control that opened the dialog, if it is still on the page. Otherwise the next row, the previous row, then the list
  • The page behind ignores the pointer, keys and screen readers, and does not scroll or shift sideways
  • The close button is named Close
  • Controls at least 44 px high on touch screens
  • In forced colors mode the panel draws a 1 px outline in the system text color
  • Escape reaches the front layer only, so a menu open inside the dialog closes before the dialog

Content

  • The title names the task or asks the question. No end punctuation except a question mark
  • The body adds only what the title cannot hold and starts with the consequence
  • Buttons answer the title with a verb, plus a noun when the verb alone is ambiguous. Cancel is always Cancel. No OK, Yes or No
  • Example: Delete this candidate permanently? / Sunita Rai's CV, passport scan and interview notes are removed now and cannot be restored / Cancel, Delete candidate
  • Save failed message: The task was not added. Try again

Tokens

  • --layer-dialog
  • --overlay
  • --glass-fill-raised
  • --glass-blur-raised
  • --glass-highlight
  • --glass-ring-raised
  • --elevation-3
  • --radius-2xl
  • --space-2
  • --space-3
  • --space-4
  • --space-5
  • --space-6
  • --type-small
  • --text
  • --text-muted
  • --border-rule
  • --error-ink
  • --duration-fast
  • --duration-base
  • --duration-slow
  • --ease-soft
  • --ease-standard
  • --focus-ring

JSON

{
  "title": "Dialog",
  "path": "/components/dialog",
  "url": "https://design.werklist.com/components/dialog",
  "text": "https://design.werklist.com/agent/components/dialog",
  "summary": "A dialog stops the page for a decision or a short task that has to come first.",
  "version": "2.1.2",
  "kind": "component",
  "name": "Dialog",
  "purpose": "Stops the page for a decision or a short task and returns focus to the control that opened it.",
  "use": [
    "A decision the page cannot go on without, such as deleting a candidate for good",
    "A short form that belongs to one moment, such as adding a task",
    "A record with its history, in the large size"
  ],
  "avoid": [
    "An action that can be undone: act at once and offer Undo in a toast",
    "Details read beside the page: use a side sheet",
    "A few controls for one item: use a popover",
    "Information that needs no answer: use a toast or an inline message",
    "A dialog opened from a dialog, except a confirmation about the dialog beneath"
  ],
  "anatomy": [
    {
      "part": "Layer",
      "measure": "covers the whole window, above the page",
      "token": "--layer-dialog"
    },
    {
      "part": "Scrim",
      "measure": "no blur, fades in over 150 ms",
      "token": "--overlay"
    },
    {
      "part": "Margin to the window",
      "measure": "24 px",
      "token": "--space-6"
    },
    {
      "part": "Panel",
      "measure": "radius 16 px, at most 92 percent of the window high",
      "token": "--glass-fill-raised, --glass-blur-raised, --elevation-3, --radius-2xl"
    },
    {
      "part": "Panel edge, dark theme",
      "measure": "a 1 px top highlight and a 0.5 px ring over the shadow. The light theme shows the shadow alone",
      "token": "--glass-highlight, --glass-ring-raised"
    },
    {
      "part": "Header",
      "measure": "padding 16 px top, 20 px left, 12 px right",
      "token": "--space-4, --space-5, --space-3"
    },
    {
      "part": "Title",
      "measure": "h2, 17 px on a 24 px line, weight 700",
      "token": "--text"
    },
    {
      "part": "Description",
      "measure": "14 px on 22 px, 4 px below the title",
      "token": "--type-small, --text-muted"
    },
    {
      "part": "Close button",
      "measure": "icon button, 32 px, 44 px on touch screens, with a 16 px cross"
    },
    {
      "part": "Body",
      "measure": "padding 8 px top, 20 px sides, 20 px bottom without a footer, 8 px with one. Scrolls on its own, and the page behind stays still",
      "token": "--space-2, --space-5, --type-small, --text"
    },
    {
      "part": "Footer",
      "measure": "padding 16 px top and bottom, 20 px sides. Actions at the end, 8 px apart",
      "token": "--space-4, --space-5, --space-2"
    },
    {
      "part": "Footer buttons",
      "measure": "36 px high, 44 px on touch screens"
    },
    {
      "part": "Line while scrolled",
      "measure": "1 px under the header while content is hidden above, above the footer while content is hidden below",
      "token": "--border-rule"
    }
  ],
  "variants": [
    {
      "name": "Standard",
      "use": "A form or content. role dialog, close button, closes on a scrim click when nothing is typed"
    },
    {
      "name": "Confirmation",
      "use": "A question before an action that cannot be undone. role alertdialog, small size, no close button, the scrim does nothing, focus starts on Cancel, the verb takes the danger button when it deletes"
    }
  ],
  "sizes": [
    {
      "name": "Small, 448 px wide: confirmations and notices"
    },
    {
      "name": "Medium, 512 px wide: forms"
    },
    {
      "name": "Large, 896 px wide: a record with its history"
    },
    {
      "name": "Below 640 px: a bottom sheet. Large fills the screen inside the safe areas"
    }
  ],
  "states": [
    {
      "name": "Opening",
      "change": "The scrim fades in over 150 ms. The panel fades in, grows from 97 percent and rises 8 px, over 200 ms",
      "tokens": [
        "--duration-fast",
        "--duration-base",
        "--ease-soft"
      ]
    },
    {
      "name": "Closing",
      "change": "Reverse, over 150 ms",
      "tokens": [
        "--duration-fast",
        "--ease-standard"
      ]
    },
    {
      "name": "Reduced motion",
      "change": "Opacity only",
      "tokens": [
        "--duration-fast"
      ]
    },
    {
      "name": "Scrolled",
      "change": "The 1 px line shows under the header or above the footer while content is hidden behind it",
      "tokens": [
        "--border-rule"
      ]
    },
    {
      "name": "Busy",
      "change": "The completing button is busy at once (aria-busy, presses ignored) and shows its spinner after 300 ms. The rest of the dialog stays usable",
      "tokens": [
        "--duration-slow"
      ]
    },
    {
      "name": "Save failed",
      "change": "A message in the error tone at the top of the body, role alert. The entries stay as typed and focus stays on the button",
      "tokens": [
        "--error-ink"
      ]
    },
    {
      "name": "Invalid entry",
      "change": "After a failed submit, focus moves to the first field with an error, and its message sits under that field"
    },
    {
      "name": "Content loading",
      "change": "Header and footer show at once over a skeleton of the body"
    },
    {
      "name": "Unsaved entries",
      "change": "Escape, the close button, a scrim click and a swipe open the confirmation Discard the changes? with Keep editing and Discard. Cancel closes without asking"
    }
  ],
  "keys": [
    {
      "key": "Tab",
      "action": "Next control. From the last back to the first"
    },
    {
      "key": "Shift+Tab",
      "action": "Previous control. From the first to the last"
    },
    {
      "key": "Enter in a single line field",
      "action": "Submits the form"
    },
    {
      "key": "Enter or Space on a button",
      "action": "Activates it"
    },
    {
      "key": "Escape",
      "action": "Closes the front layer only. Asks first when entries are unsaved. In a confirmation, answers Cancel"
    }
  ],
  "accessibility": [
    "role dialog or alertdialog, aria-modal true, aria-labelledby the title. Aria-describedby the body of a confirmation",
    "Focus on open: the first field of a form, Cancel in a confirmation, the panel itself (tabindex -1) when it only shows content",
    "Tab reaches only the controls that show in the dialog",
    "Focus on close: the control that opened the dialog, if it is still on the page. Otherwise the next row, the previous row, then the list",
    "The page behind ignores the pointer, keys and screen readers, and does not scroll or shift sideways",
    "The close button is named Close",
    "Controls at least 44 px high on touch screens",
    "In forced colors mode the panel draws a 1 px outline in the system text color",
    "Escape reaches the front layer only, so a menu open inside the dialog closes before the dialog"
  ],
  "content": [
    "The title names the task or asks the question. No end punctuation except a question mark",
    "The body adds only what the title cannot hold and starts with the consequence",
    "Buttons answer the title with a verb, plus a noun when the verb alone is ambiguous. Cancel is always Cancel. No OK, Yes or No",
    "Example: Delete this candidate permanently? / Sunita Rai's CV, passport scan and interview notes are removed now and cannot be restored / Cancel, Delete candidate",
    "Save failed message: The task was not added. Try again"
  ],
  "tokens": [
    "--layer-dialog",
    "--overlay",
    "--glass-fill-raised",
    "--glass-blur-raised",
    "--glass-highlight",
    "--glass-ring-raised",
    "--elevation-3",
    "--radius-2xl",
    "--space-2",
    "--space-3",
    "--space-4",
    "--space-5",
    "--space-6",
    "--type-small",
    "--text",
    "--text-muted",
    "--border-rule",
    "--error-ink",
    "--duration-fast",
    "--duration-base",
    "--duration-slow",
    "--ease-soft",
    "--ease-standard",
    "--focus-ring"
  ]
}

Raw file: https://design.werklist.com/agent/components/dialog