eBay

Dialog

Screenshot of generic web dialog

Introduction

In traditional desktop operating systems, a dialog is a child-window that typically will either communicate information to the user, display a prompt for input, or allow the user to verify or cancel an action.

Whilst these cases are also valid and true on the web, we also see much more generic use of the dialog (for better or worse). For example, a dialog itself might contain an entire full page-like experience, navigation or settings.

This "generic" dialog is discussed here, and falls under the umbrella of progressive disclosure, but also familiarise yourself with the following, more specific dialog patterns:

TIP: On large screens, dialogs are typically "light-boxed" in the center or "panelled" on one edge, with a mask covering the remaining portion of the screen. On small screens however, the dialog may cover the entire screen.

Screenshot of various fullscreen dialogs on mobile device

Working Examples

You can take a look at the generic dialog pattern in action on our examples site.

You can get an idea of the required markup structure by viewing our bones site.


Terminology

dialog
the pattern as a whole, containing button, overlay and mask
button
the button that opens the overlay
overlay/window
contains the actual content
title
the title of the dialog
mask
an element that visibly masks the content "behind" the window

We use the terms "overlay" and "window" interchangeably. They mean the same thing.


Best Practices

A dialog is typically opened in one of two ways:

  • Click-activated: explicitly opened and closed by clicking the button
  • System-activated: automatically opened by the system/application on page load or at some other arbitrary time

Opening dialogs that are not requested by user (i.e. system-activated) are a violation of WCAG Guideline 3.2.5 (Level AAA) and therefore should be reserved for exceptional circumstances only (i.e. not ads!).

Overlay must not be opened on hover or focus of button (or any other element).

Overlay must be opened on click-event of button or via an application event.

Overlay must use ARIA role of dialog (not alertdialog in this context).

Overlay must contain a button that can dismiss the dialog (this will be 'close', 'cancel' or 'okay', depending on the specific type of dialog).

Overlay must contain at least one interactive element (this can be the close button).

Overlay must be labelled by an onscreen element (a heading for example) or explicit attribute.

Overlay must start heading hierarchy at level 2.

Mask must be positioned between page and overlay.

If the overlay contains critical content, the content must be available without JavaScript. For example, the content could exist either at an alternative URL or elsewhere on the same page.

Interaction Design

This section provides interaction design for keyboard, screen reader and pointing devices.

Keyboard

Button must be keyboard focusable.

If button has focus, ENTER or SPACE key must open overlay.

When overlay opens, focus must move to an interactive element inside of overlay (this is usually the close button).

Overlay must confine TAB and SHIFT-TAB to it's focusable children.

Pressing ESC key may close overlay.

Button must receive focus when overlay is closed.

Screen Reader

Button must not announce 'has popup'. The attribute aria-haspopup is not valid in this context (i.e. a dialog is not considered a popup).

Title must be announced when overlay opens (via focus management).

Focussed element must be announced when overlay opens (via focus management).

Overlay must confine virtual cursor to it's child elements.

Assistive technology might announce "Entering/leaving dialog" (or words to those affect) when focus enters or leaves the overlay respectively.

Pointer

Clicking button must open overlay.

Clicking mask can dismiss overlay (same functionality as 'close' button).

Developer Guide

HTML does provide a <dialog> tag, but at the time of writing this tag is experimental, and only supported in Chrome and Opera.

Content (HTML)

We are going to create a click-activated dialog. This will require a button (the thing we click on )

Button

We are going to add a button element opens the overlay when clicked:

<button class="dialog-button" type="button">Open Dialog</button>

Dialog Role

The root element must have a role of dialog.

<div role="dialog">

</div>

Hidden State

The dialog must have a property of hidden to prevent it from being displayed on page load.

<div hidden role="dialog">

</div>

To display the dialog, simply remove this property. We will later show how to use this property in conjunction with CSS transitions and JavaScript.

Window

The window is the actual overlay that contains the dialog content.

<div hidden role="dialog">
    <div class="dialog__window" role="document">
        <!-- dialog content goes here -->
    </div>
</div>

To assist older screen reader versions (NVDA in particular), any immediate child of the dialog role must have a document role (if it contains content). In time, as screen reader support improves, the requirement for this role may go away.

Mask

If the window does not cover the full screen, which is usually the case on desktop, a mask will be needed to prevent clicks on the page content behind the window.

<div hidden role="dialog">
    <div class="dialog__window" role="document">
        <!-- dialog content goes here -->
    </div>
    <div class="dialog__mask"></div>
</div>

By keeping the window and mask as separate children of the root element, we will be able to independently control their transitions in CSS. More on that later.

NOTE: Because the mask does not contain content, it does not need a document role.

Header and Body

Next we create the header and body structure:

<div hidden role="dialog">
    <div class="dialog__window" role="document">
        <header role="banner">

        </header>
        <div>

        </div>
    </div>
    <div class="dialog__mask"></div>
</div>

Title

Every dialog must be labelled. We can use aria-labelledby to point to a suitable labelling element inside of our dialog.

<div aria-labelledby="dialog_title" hidden role="dialog">
    <div class="dialog__window" role="document">
        <header role="banner">
          <h2 id="dialog_title">Dialog Example</h2>
        </header>
        <div>
            <!-- dialog contents go here -->
        </div>
    </div>
    <div class="dialog__mask"></div>
</div>

Alternatively, if no suitable heading or labelling element exists, we can specify the label in an aria-label property instead:

<div aria-label="Dialog Example" hidden role="dialog">

Close Button

Every dialog requires some form of close button. For a generic dialog, this button should be placed in the header.

<div hidden role="dialog">
    <div class="dialog__window" role="document">
        <header role="banner">
          <h2 id="dialog_title">Dialog Example</h2>
          <button aria-label="Close Dialog">X</button>
        </header>
        <div>
            <!-- dialog contents go here -->
        </div>
    </div>
    <div class="dialog__mask"></div>
</div>

A more specific type of dialog may feature a different, and more prominent, way to dismiss a dialog. For example, an 'Okay' button in an alert dialog, or a 'Cancel' button in a confirmation dialog. In such cases the close button can be omitted from the header.

Presentation (CSS)

Once we have our markup in place, there are many different ways to visually, and uniquely, style a dialog.

The most important aspects for accessibility, are the mask and the hidden state.

Hidden Polyfill

First of all, let's provide a "polyfill" for older browsers that do not support the hidden attribute.

[hidden] {
    display: none;
}

Mask

The mask creates a visual distinction between the overlay content in the foreground, and the page content in the background.

It must also prevent pointing devices from interacting with the page content behind the window.

.dialog__mask {
  background-color: #000000;
  height: 100%;
  left: 0;
  opacity: 0.6;
  overflow: hidden;
  position: fixed;
  top: 0;
  width: 100%;
  z-index: 100;
}

It is critical that the window has a higher z-index than the mask!

NOTE: the mask does not prevent keyboard or screen reader from accessing the page content behind the window (we need JavaScript for that).

Behaviour (JS)

No matter how a dialog is opened, and how it is styled, it will always have the same accessibility requirements once in the open state.

The goal is to confine keyboard focus, confine the screen reader virtual cursor, and allow the user to close the dialog and return to their previous page position.

Trap Keyboard Focus

While open, a dialog must prevent keyboard focus moving from overlay to the page. In addition, keyboard focus must wrap from the last interactive element back to the first, and vice versa.

There are several different JavaScript techniques to achieve such behaviour. We provide an example jquery-keyboard-trap plugin as an example.

Trap Screen Reader

While open, any content that is not inside of the overlay must be hidden from screen reader users.

This can be achieved by applying aria-hidden="true" to the relevant nodes in the DOM.

You must also remember to unhide these elements when the dialog is closed, otherwise the page will be completely hidden & inaccessible to screen readers!

Again, we provide an example jquery-screenreader-trap plugin to assist with this behaviour.

Prevent Page Scroll

It is desirable to prevent the background page from scrolling when interacting with the foreground dialog content. This can be achieved by simply applying overflow:hidden to the html body:

body.has-dialog {
    overflow: hidden;
}

The presence of this class would be toggled by our JavaScript on dialog hide/show event.

ESC Key

Pressing the ESC key can close the overlay:

$(document).on('keyup', function onDocumentKeyup(e) {
    if (e.keyCode === 27) {
        $dialog.trigger('close');
    }
});

$dialog.on('close', function onDialogClose() {
    $dialog.detach();
    $dialog__button.focus();
    $('body').removeClass('has-dialog');
});

Note that if the overlay was opened by clicking a button, we must set keyboard focus back to that button.

Useful Plugins

We have some experimental jQuery plugins that may assist you with creation of an accessible dialog widget:

ARIA Reference

This section gives an overview of ARIA usage within the context of this pattern.

role=dialog

Informs the assistive technology that the user is inside of a dialog.

role=document

For backwards compatibility for older screen readers. Without this role they cannot navigate the virtual cursor inside of dialog window.

role=banner

Identifies the banner/header section of the dialog. A user knows that this area typically contains the dialog heading and close button.

aria-labelledby

Informs the assistive technology of the onscreen text used to label the dialog.

aria-label

Sets an explicit label value for the dialog, overriding any onscreen labelling text.

aria-hidden

While the dialog is in an open state, aria-hidden is used to hide all non-dialog elements from assistive technology.

results matching ""

    No results matching ""