My Design System

Dialog

Dialog is a small window that can be used to present information and user interface elements in an overlay.

Open in a
new tab
import { css, html, LitElement } from 'lit';
import '@vaadin/vaadin-notification/vaadin-notification';
import '@vaadin/vaadin-button/vaadin-button';
import '@vaadin/vaadin-text-field/vaadin-text-field';
import '@vaadin/vaadin-ordered-layout/vaadin-horizontal-layout';
import '@vaadin/vaadin-ordered-layout/vaadin-vertical-layout';

export class Example extends LitElement {
  static get styles() {
    return css`
      :host {
        display: flex !important;
        justify-content: center;
        background-color: var(--lumo-shade-20pct);
        padding: var(--lumo-space-l);
        pointer-events: none;
        user-select: none;
        -webkit-user-select: none;
      }

      .overlay {
        display: flex;
        justify-content: center;
        outline: none;
        -webkit-tap-highlight-color: transparent;
        background-color: var(--lumo-base-color);
        background-image: linear-gradient(var(--lumo-tint-5pct), var(--lumo-tint-5pct));
        border-radius: var(--lumo-border-radius-m);
        box-shadow: 0 0 0 1px var(--lumo-shade-5pct), var(--lumo-box-shadow-m);
        color: var(--lumo-body-text-color);
        font-family: var(--lumo-font-family);
        font-size: var(--lumo-font-size-m);
        font-weight: 400;
        line-height: var(--lumo-line-height-m);
        letter-spacing: 0;
        text-transform: none;
        -webkit-text-size-adjust: 100%;
        -webkit-font-smoothing: antialiased;
      }

      .content {
        padding: var(--lumo-space-l);
        width: 300px;
        max-width: 100%;
      }
    `;
  }
  render() {
    return html`
      <div class="overlay">
        <div class="content">
          <vaadin-vertical-layout theme="spacing" style="align-items: stretch;">
            <h2 style="margin: var(--lumo-space-m) 0 0 0;">New employee</h2>
            <vaadin-vertical-layout style="align-items: stretch;">
              <vaadin-text-field label="First name"></vaadin-text-field>
              <vaadin-text-field label="Last name"></vaadin-text-field>
            </vaadin-vertical-layout>
            <vaadin-horizontal-layout theme="spacing" style="justify-content: flex-end">
              <vaadin-button>Cancel</vaadin-button>
              <vaadin-button theme="primary">Save changes</vaadin-button>
            </vaadin-horizontal-layout>
          </vaadin-vertical-layout>
        </div>
      </div>
    `;
  }
}
Open in a
new tab
The inline `.renderer` function is encapsulated by the `guard` directive for performance reasons.
See the https://lit.dev/docs/templates/directives#guard[official Lit documentation] for more details.

<vaadin-dialog
  aria-label="Create new employee"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="width: 300px; max-width: 100%; align-items: stretch;"
        >
          <h2 style="margin: var(--lumo-space-m) 0 0 0; font-size: 1.5em; font-weight: bold;">
            Create new employee
          </h2>
          <vaadin-vertical-layout style="align-items: stretch;">
            <vaadin-text-field label="First name"></vaadin-text-field>
            <vaadin-text-field label="Last name"></vaadin-text-field>
          </vaadin-vertical-layout>
          <vaadin-horizontal-layout theme="spacing" style="justify-content: flex-end">
            <vaadin-button @click="${() => (this.dialogOpened = false)}">
              Cancel
            </vaadin-button>
            <vaadin-button theme="primary" @click="${() => (this.dialogOpened = false)}">
              Save
            </vaadin-button>
          </vaadin-horizontal-layout>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

Modality

A modal Dialog blocks the user from interacting with the rest of the user interface while the Dialog is open, as opposed to a non-modal Dialog which does not block interaction.

Dialogs are modal by default.

Use modal Dialogs for:

  • Displaying important information, like system errors

  • Requesting user input as part of a workflow, for example, an edit Dialog

  • Confirmation of irreversible actions, such as deleting data (Confirm Dialog is a convenient alternative for these use cases)

  • Breaking out sub-tasks into a separate user interface

Use non-modal Dialogs:

  • When the user needs access to the content below the Dialog

  • For less critical, optional, and/or support tasks

<vaadin-dialog modeless>...</vaadin-dialog>

Non-modal Dialogs should in most cases be draggable, so that the user can move them to access the user interface beneath.

Draggable

Dialogs can be made draggable, enabling the user to move them around using a pointing device.

It is recommended to make non-modal Dialogs draggable so that the user can interact with content that might otherwise be obscured by the Dialog. For example, a Dialog for taking notes, or adding widgets to a dashboard using drag and drop, can offer a better experience by allowing the user to move the Dialog around.

Modal Dialogs don’t benefit from being draggable as their modality curtain (the dark overlay behind the dialog) obscures the underlying user interface.

Open in a
new tab
The inline `.renderer` function is encapsulated by the `guard` directive for performance reasons.
See the https://lit.dev/docs/templates/directives#guard[official Lit documentation] for more details.

<vaadin-dialog
  aria-label="Add note"
  draggable
  modeless
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="width: 300px; max-width: 100%; align-items: stretch;"
        >
          <vaadin-horizontal-layout
            class="draggable"
            style="border-bottom: 1px solid var(--lumo-contrast-20pct); cursor: move; padding: var(--lumo-space-m) var(--lumo-space-l); margin: calc(var(--lumo-space-s) * -1) calc(var(--lumo-space-l) * -1) 0"
          >
            <h2 style="margin: 0; font-size: 1.5em; font-weight: bold;">Add note</h2>
          </vaadin-horizontal-layout>
          <vaadin-vertical-layout style="align-items: stretch;">
            <vaadin-text-field label="Title"></vaadin-text-field>
            <vaadin-text-area label="Description"></vaadin-text-area>
          </vaadin-vertical-layout>
          <vaadin-horizontal-layout theme="spacing" style="justify-content: flex-end">
            <vaadin-button @click="${() => (this.dialogOpened = false)}">
              Cancel
            </vaadin-button>
            <vaadin-button theme="primary" @click="${() => (this.dialogOpened = false)}">
              Add note
            </vaadin-button>
          </vaadin-horizontal-layout>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

By default, the outer edges of a Dialog, as well as the space between its components, can be used to move the Dialog around.

Any component contained within a Dialog can be marked and used as a drag handle by applying the draggable class name to it. You can choose whether or not to make the component’s content draggable as well, or just the component itself.

Resizable

A resizable Dialog allows the user to resize the Dialog by dragging from the edges of the Dialog with a pointing device. Dialogs are not resizable by default.

Dialogs containing dynamic content and/or a lot of information, such as complex forms or Grids, can benefit from being resizable as it offers the user some flexibility as to how much data is visible at once. It also gives the user control over which part of the underlying user interface is obscured.

Dialogs that contain very little or compact information do not need to be resizable.

Open in a
new tab
The inline `.renderer` function is encapsulated by the `guard` directive for performance reasons.
See the https://lit.dev/docs/templates/directives#guard[official Lit documentation] for more details.

<vaadin-dialog
  aria-label="Employee list"
  resizable
  draggable
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="max-width: 100%; min-width: 300px; height: 100%; align-items: stretch;"
        >
          <h2 style="margin: var(--lumo-space-m) 0 0 0; font-size: 1.5em; font-weight: bold;">
            Employee list
          </h2>
          <vaadin-grid .items="${this.people}">
            <vaadin-grid-column path="firstName" title="First name"></vaadin-grid-column>
            <vaadin-grid-column path="lastName" title="Last name"></vaadin-grid-column>
            <vaadin-grid-column path="email" title="Email"></vaadin-grid-column>
            <vaadin-grid-column path="profession" title="Profession"></vaadin-grid-column>
            <vaadin-grid-column path="membership" title="Membership"></vaadin-grid-column>
          </vaadin-grid>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

Closing

Modal Dialogs are closable in three ways:

  1. Pressing the Esc key

  2. Clicking outside the Dialog

  3. Programmatically, for example through the click of a Button

Providing an explicit button for closing a Dialog is recommended.

Open in a
new tab
The inline `.renderer` function is encapsulated by the `guard` directive for performance reasons.
See the https://lit.dev/docs/templates/directives#guard[official Lit documentation] for more details.

<vaadin-dialog
  aria-label="System maintenance notice"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="width: 300px; max-width: 100%; align-items: stretch;"
        >
          <h2 style="margin: var(--lumo-space-m) 0; font-size: 1.5em; font-weight: bold;">
            System maintenance
          </h2>
          <p>
            System maintenance will begin at 3 PM. It is schedule to conclude at 5PM. We
            apologise for any inconvenience.
          </p>
          <vaadin-button
            @click="${() => (this.dialogOpened = false)}"
            style="align-self: flex-end;"
          >
            Close
          </vaadin-button>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

Layout and Scrolling

Dialogs automatically become scrollable when their content overflows. Custom scrollable areas can be created using the Scroller component.

Removing the Padding

The Dialog’s built-in padding can be removed by applying the no-padding theme variant, for example to create headers or footers that use the full width of the dialog.

Open in a
new tab
The inline `.renderer` function is encapsulated by the `guard` directive for performance reasons.
See the https://lit.dev/docs/templates/directives#guard[official Lit documentation] for more details.

<vaadin-dialog
  theme="no-padding"
  aria-label="Create new employee"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          style="align-items: stretch; height: 100%; max-height: 420px; width: 320px;"
        >
          <header
            class="draggable"
            style="border-bottom: 1px solid var(--lumo-contrast-10pct); padding: var(--lumo-space-m) var(--lumo-space-l);"
          >
            <h2
              style="font-size: var(--lumo-font-size-xl); font-weight: 600; line-height: var(--lumo-line-height-xs); margin: 0;"
            >
              Create new employee
            </h2>
          </header>
          <vaadin-scroller scroll-direction="vertical" style="padding: var(--lumo-space-l);">
            <vaadin-vertical-layout
              aria-labelledby="personal-title"
              role="region"
              style="align-items: stretch; margin-bottom: var(--lumo-space-xl);"
            >
              <h3
                id="personal-title"
                style="font-size: var(--lumo-font-size-l); font-weight: 600; line-height: var(--lumo-line-height-xs); margin: 0 0 var(--lumo-space-s) 0;"
              >
                Personal information
              </h3>
              <vaadin-text-field label="First name"></vaadin-text-field>
              <vaadin-text-field label="Last name"></vaadin-text-field>
              <vaadin-date-picker
                initial-position="1990-01-01"
                label="Birthdate"
              ></vaadin-date-picker>
            </vaadin-vertical-layout>
            <vaadin-vertical-layout
              aria-labelledby="employment-title"
              role="region"
              style="align-items: stretch;"
            >
              <h3
                id="employment-title"
                style="font-size: var(--lumo-font-size-l); font-weight: 600; line-height: var(--lumo-line-height-xs); margin: 0 0 var(--lumo-space-s) 0;"
              >
                Employment information
              </h3>
              <vaadin-text-field label="Position"></vaadin-text-field>
              <vaadin-text-area label="Additional information"></vaadin-text-area>
            </vaadin-vertical-layout>
          </vaadin-scroller>
          <footer
            style="background-color: var(--lumo-contrast-5pct); padding: var(--lumo-space-s) var(--lumo-space-m); text-align: right;"
          >
            <vaadin-button
              theme="tertiary"
              style="margin-inline-end: var(--lumo-space-m);"
              @click="${() => (this.dialogOpened = false)}"
            >
              Cancel
            </vaadin-button>
            <vaadin-button theme="primary" @click="${() => (this.dialogOpened = false)}">
              Save
            </vaadin-button>
          </footer>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

Best Practises

Use Sparingly

Dialogs are disruptive by nature and should be used sparingly. Do not use them to communicate nonessential information, such as success messages like “Logged in”, “Copied”, and so on. Instead, use Notifications when appropriate.

Button Placement

ComponentUsage recommendations

Confirm Dialog

Dialog for confirming user actions and decisions