Accessibility pattern · Overlays

Non-modal dialog

show() opens a dialog without making the page inert, so people can read the help and keep typing in the form beside it. Focus moves into the panel when it opens and back when it closes; while it is open, F6 or a pair of buttons moves between the two.

WCAG criteria
6
Keyboard rules
4
Checked with
axe, keyboard and the inspector

Live demo

Try it

Use it with a mouse, a keyboard or a screen reader. The inspector beside it shows what the browser tells assistive technology as you go: focus, state changes and announcements.

Delivery address

Address help

  • Your PIN code is on any parcel sent to this address.
  • Put the flat number first, so the rider finds the door.

or press F6

The demo works without JavaScript only as far as its HTML does; the inspector needs JavaScript.

Keyboard

Keys it answers to

Every action works without a pointer.

KeyWhat it does
Enter or SpaceOn Help, opens the panel and moves focus into it. While the panel is open, the same button reads Go to help and takes focus back into it.
F6 or ShiftF6While the panel is open, moves focus between the panel and the page, back to where it was in each.
Tab or ShiftTabMoves through the page and the panel in reading order; the panel never traps focus.
EscapeWith focus in the panel, closes it and returns focus to the Help button.

Screen readers

What it announces

Written from the roles, names and states in the markup.

WhenExpected announcement
Focus reaches the Help buttonHelp, button, opens a dialog
The panel opensAddress help, dialog. Address help, heading level 3
F6 or Back to page, after typing in the PIN code fieldPIN code, edit text, 400607
Focus reaches the page's button while the panel is openGo to help, button, opens a dialog
A question is sentSent. We reply by email within a day.
Escape closes the panelHelp, button, opens a dialog

These are expected announcements, not recordings. Wording and order differ between screen readers and browsers.

Code

Copy the code

The exact files this demo runs on. The styles are served with a prefix that keeps this site's own styles out of the demo; what you copy is the original.

The markup

<div class="ap-nmd" data-ap-nmd>
  <div class="ap-nmd__layout">
    <form class="ap-nmd__page" data-ap-nmd-page>
      <div class="ap-nmd__top">
        <h3 class="ap-nmd__name">Delivery address</h3>
        <button type="button" class="ap-btn ap-nmd__help" data-ap-nmd-open aria-haspopup="dialog">
          <svg class="ap-btn__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.3a2.5 2.5 0 1 1 3.3 2.4c-.6.3-.9.8-.9 1.4v.4"/><path d="M12 17h.01"/></svg>
          <span data-ap-nmd-label>Help</span>
        </button>
      </div>
      <div class="ap-field">
        <label class="ap-label" for="nmd-line1">Flat and building</label>
        <input class="ap-input" id="nmd-line1" name="line1" autocomplete="address-line1" value="B-1204, Silver Oak Residency" />
      </div>
      <div class="ap-nmd__pair">
        <div class="ap-field">
          <label class="ap-label" for="nmd-pin">PIN code</label>
          <input class="ap-input" id="nmd-pin" name="pin" inputmode="numeric" autocomplete="postal-code" value="400607" />
        </div>
        <div class="ap-field">
          <label class="ap-label" for="nmd-city">City</label>
          <input class="ap-input" id="nmd-city" name="city" autocomplete="address-level2" value="Thane" />
        </div>
      </div>
      <div class="ap-nmd__foot">
        <button type="submit" class="ap-btn ap-btn--primary">Save address</button>
        <p class="ap-nmd__saved" role="status"></p>
      </div>
    </form>

    <dialog class="ap-nmd__panel" aria-labelledby="nmd-name">
      <div class="ap-nmd__head">
        <span class="ap-nmd__badge" aria-hidden="true">
          <svg viewBox="0 0 24 24" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M9.6 9.3a2.5 2.5 0 1 1 3.3 2.4c-.6.3-.9.8-.9 1.4v.4"/><path d="M12 17h.01"/></svg>
        </span>
        <h3 class="ap-nmd__pname" id="nmd-name" tabindex="-1" autofocus>Address help</h3>
        <button type="button" class="ap-nmd__close" data-ap-nmd-close aria-label="Close help">
          <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 6l12 12M18 6 6 18"/></svg>
        </button>
      </div>
      <ul class="ap-nmd__tips">
        <li>Your PIN code is on any parcel sent to this address.</li>
        <li>Put the flat number first, so the rider finds the door.</li>
      </ul>
      <form class="ap-nmd__ask" data-ap-nmd-ask>
        <label class="ap-label" for="nmd-question">Ask a question</label>
        <div class="ap-nmd__row">
          <input class="ap-input" id="nmd-question" name="question" autocomplete="off" />
          <button type="submit" class="ap-btn ap-btn--primary">Send</button>
        </div>
        <p class="ap-nmd__status" role="status"></p>
      </form>
      <div class="ap-nmd__back">
        <button type="button" class="ap-btn ap-btn--ghost" data-ap-nmd-back>
          <svg class="ap-btn__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M19 12H5"/><path d="m11 18-6-6 6-6"/></svg>
          Back to page
        </button>
        <p class="ap-nmd__keys">or press <kbd>F6</kbd></p>
      </div>
    </dialog>
  </div>
</div>

WCAG 2.2

What it meets

The success criteria this pattern takes care of, and how.

  • 2.1.1 Keyboard Level A

    Opening, closing and moving between the panel and the page all work from the keyboard, with buttons for each as well as keys.

  • 2.1.2 No Keyboard Trap Level A

    The panel is not modal, so Tab moves out of it into the rest of the page as it would from any other content.

  • 2.4.3 Focus Order Level A

    Focus goes into the panel on open, back to the Help button on close, and F6 returns to the exact control left in each part.

  • 2.4.11 Focus Not Obscured (Minimum) Level AA

    On wide screens the panel sits beside the form and on narrow ones below it, so it never covers the field that has focus.

  • 4.1.2 Name, Role, Value Level A

    The dialog element gives role=dialog, its heading names it, and the Help button says it opens a dialog.

  • 4.1.3 Status Messages Level AA

    Saving the address and sending a question are confirmed in status messages, announced without moving focus.

Usage

When to use it

Use it

  • Help, notes or tools that people consult while they keep working on the page.
  • Panels that can stay open alongside the work, like a chat window or a find-and-replace bar.

Use something else

  • A task that has to be finished or dismissed before going on: use a modal dialog.
  • A question that must be answered now, like confirming a delete: use an alert dialog.
  • A small panel that belongs to one button and closes when you click away: use a popover.

Common failures

How it usually goes wrong

  • Modal by accident

    Opening a help panel with showModal() makes the form it is helping with unreachable. show() leaves the page working.

  • aria-modal on a panel that is not modal

    aria-modal=true tells screen readers to hide the page, while Tab still goes there. Leave it off; the dialog element sets it only for showModal().

  • Focus left on the page

    If focus stays on the button, screen reader users do not know the panel opened, and keyboard users must Tab through the whole page to reach it.

  • No quick way back

    With the panel open, getting between it and the page by Tab alone can take dozens of presses. F6 and the Back to page button take one.

  • Covering what it helps with

    A floating panel over the form hides the very field people are filling in. Here it sits beside the form, or below it on a narrow screen.

Notes

Building it

  • show() opens the dialog without inertness or a backdrop; the element still has role=dialog, and aria-labelledby pointing at its heading names it.
  • The panel's heading has tabindex=-1 and autofocus, so the browser puts focus there on show(): reading starts at the panel's name instead of skipping the tips to reach the first field.
  • Escape is not built in for non-modal dialogs. The script closes on Escape only when focus is inside the panel, so the page's own fields keep the key.
  • Browsers use F6 to reach the address bar, so the script takes it only while the panel is open and focus is in the demo. The Back to page and Go to help buttons do the same job for anyone who does not know the key.

Sources: HTML: the dialog element · WAI-ARIA Authoring Practices: Developing a Keyboard Interface

Checked with axe in light and dark themes, at desktop and phone widths, and by keyboard. Not yet tested with every screen reader and browser pair. Report a correction