Accessibility pattern · Forms and flows

Session timeout warning

Before an idle session ends, an alert dialog asks whether you are still there, with focus on Stay signed in and at least 20 seconds to answer. The time left is spoken once, when the warning opens, while the countdown on screen keeps going quietly; if time runs out, what you typed is still there when you sign in again.

WCAG criteria
6
Keyboard rules
5
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.

Demo clock

Time until sign-out
Stopped
Times extended
0

Contact support

Signed in as Asha Rao

For your security, you are signed out after a while with no activity. You are warned first, and what you type here is kept.

A few lines are enough. We reply within one working day.

Are you still there?

Nothing has happened for a while, so you will be signed out to keep your account safe.

Time left: 20 seconds

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 Start the clock, starts the idle clock; on Skip to the warning, opens the warning at once.
Enter or SpaceIn the warning, Stay signed in (focused when it opens) restarts the clock; Sign out ends the session.
Tab or ShiftTabMoves between the warning's two buttons only; the page behind cannot be reached.
EscapeCloses the warning and keeps you signed in, like Stay signed in.
Enter or SpaceAfter expiry, Sign in again (which has focus) restores the session with the form as you left it.

Screen readers

What it announces

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

WhenExpected announcement
The warning opensAre you still there?, alert dialog. Nothing has happened for a while, so you will be signed out to keep your account safe. Time left: 20 seconds. Stay signed in, button
The countdown keeps goingNothing: the number changes on screen without being announced
Stay signed in is pressedYou are still signed in.
Time runs outYou were signed out because nothing happened for a while. Your request is kept: sign in again to carry on. Sign in again, button
Sign in again is pressedSigned in again. Your request is as you left it.

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-session-timeout" data-ap-session-timeout>
  <div class="ap-session-timeout__demo" role="group" aria-labelledby="st-demo-name">
    <p class="ap-session-timeout__kicker" id="st-demo-name">Demo clock</p>
    <div class="ap-session-timeout__controls">
      <div class="ap-field ap-session-timeout__length">
        <label class="ap-label" for="st-length">Session length</label>
        <select class="ap-input" id="st-length" data-ap-length>
          <option value="30" selected>30 seconds</option>
          <option value="60">1 minute</option>
          <option value="120">2 minutes</option>
        </select>
      </div>
      <button type="button" class="ap-btn" data-ap-start>
        <svg class="ap-btn__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M7 5.5v13l11-6.5Z"/></svg>
        Start the clock
      </button>
      <button type="button" class="ap-btn ap-btn--ghost" data-ap-skip>Skip to the warning</button>
    </div>
    <dl class="ap-session-timeout__readout">
      <div><dt>Time until sign-out</dt><dd data-ap-clock>Stopped</dd></div>
      <div><dt>Times extended</dt><dd data-ap-extended>0</dd></div>
    </dl>
  </div>

  <section class="ap-session-timeout__app" aria-labelledby="st-app-name">
    <div class="ap-session-timeout__head">
      <span class="ap-session-timeout__avatar" aria-hidden="true">AR</span>
      <div>
        <h3 class="ap-session-timeout__name" id="st-app-name">Contact support</h3>
        <p class="ap-session-timeout__sub">Signed in as Asha Rao</p>
      </div>
    </div>

    <div class="ap-session-timeout__news">
      <svg class="ap-session-timeout__lock" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><rect x="5" y="10.5" width="14" height="10" rx="2"/><path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5"/></svg>
      <p class="ap-session-timeout__status" role="status"></p>
      <div class="ap-session-timeout__out" data-ap-out hidden>
        <button type="button" class="ap-btn ap-btn--primary" data-ap-sign-in>Sign in again</button>
      </div>
    </div>

    <form class="ap-session-timeout__form" data-ap-work>
      <p class="ap-session-timeout__note">For your security, you are signed out after a while with no activity. You are warned first, and what you type here is kept.</p>
      <div class="ap-session-timeout__pair">
        <div class="ap-field">
          <label class="ap-label" for="st-order">Order number</label>
          <input class="ap-input" id="st-order" name="order" type="text" inputmode="numeric" autocomplete="off" value="40917" />
        </div>
        <div class="ap-field">
          <label class="ap-label" for="st-topic">Topic</label>
          <select class="ap-input" id="st-topic" name="topic">
            <option>Delivery</option>
            <option selected>Damaged item</option>
            <option>Refund</option>
            <option>Something else</option>
          </select>
        </div>
      </div>
      <div class="ap-field">
        <label class="ap-label" for="st-message">What happened?</label>
        <p class="ap-hint" id="st-message-hint">A few lines are enough. We reply within one working day.</p>
        <textarea class="ap-input" id="st-message" name="message" rows="3" aria-describedby="st-message-hint"></textarea>
      </div>
      <div class="ap-session-timeout__foot">
        <button type="submit" class="ap-btn ap-btn--primary">Send request</button>
      </div>
    </form>
  </section>

  <dialog class="ap-session-timeout__dialog" role="alertdialog" aria-labelledby="st-warn-name" aria-describedby="st-warn-desc st-warn-time">
    <div class="ap-session-timeout__warn">
      <svg class="ap-session-timeout__ring" viewBox="0 0 48 48" aria-hidden="true" focusable="false">
        <circle class="ap-session-timeout__track" cx="24" cy="24" r="20"/>
        <circle class="ap-session-timeout__drain" cx="24" cy="24" r="20" pathLength="100" data-ap-ring/>
        <path class="ap-session-timeout__hands" d="M24 14v10l6 4"/>
      </svg>
      <h2 class="ap-session-timeout__ask" id="st-warn-name">Are you still there?</h2>
      <p class="ap-session-timeout__desc" id="st-warn-desc">Nothing has happened for a while, so you will be signed out to keep your account safe.</p>
      <p class="ap-session-timeout__time" id="st-warn-time"><span>Time left:</span> <span class="ap-session-timeout__count"><span data-ap-left>20</span> <span data-ap-unit>seconds</span></span></p>
      <div class="ap-session-timeout__actions">
        <button type="button" class="ap-btn" data-ap-sign-out>Sign out</button>
        <button type="button" class="ap-btn ap-btn--primary" data-ap-stay autofocus>Stay signed in</button>
      </div>
    </div>
  </dialog>
</div>

WCAG 2.2

What it meets

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

  • 2.2.1 Timing Adjustable Level A

    The warning comes at least 20 seconds before the session ends, and one key press extends it; it can be extended any number of times, more than the ten WCAG asks for.

  • 2.2.5 Re-authenticating Level AAA

    When the session does end, the form is kept as it was, so after signing in again the work carries on without loss.

  • 2.2.6 Timeouts Level AAA

    The form says up front that you are signed out after a while with no activity, and that what you type is kept.

  • 2.4.3 Focus Order Level A

    Focus moves to Stay signed in when the warning opens, back to where it was when it closes, and to Sign in again after expiry.

  • 4.1.2 Name, Role, Value Level A

    The warning is a native dialog with role=alertdialog, named by its question and described by the time left.

  • 4.1.3 Status Messages Level AA

    Staying, expiring and signing in again are confirmed in a status message, announced without moving focus.

Usage

When to use it

Use it

  • Signed-in sessions that end after a period of inactivity for security, like banking, health or account settings.
  • Any time limit you cannot remove, where people may be slowed down by reading, typing or interruptions.

Use something else

  • Time limits you do not need: the best warning is none, or a session that lasts at least 20 hours.
  • Real-time limits, like an auction closing, where extending would change the activity itself.
  • Warning someone who is busy typing: activity should keep the session alive without asking.

Common failures

How it usually goes wrong

  • Signing people out without warning

    Someone who reads slowly, uses a switch or was interrupted loses their work with no chance to act. Warn at least 20 seconds ahead and let one action extend it.

  • A countdown in a live region

    Announcing the time every second buries everything else and makes the buttons hard to use. The time is spoken once, when the warning opens.

  • Throwing away the work on expiry

    Clearing a half-written form punishes people for being slow. Keep what they typed and return them to it after they sign in again.

  • Sign out as the focused button

    A stray Enter from someone who was typing would end their session. Focus goes to Stay signed in, the action that loses nothing.

  • A warning that is not a dialog

    A banner that appears at the top of the page is missed by someone whose screen reader or magnifier is elsewhere. An alert dialog moves focus and is announced.

Notes

Building it

  • WCAG 2.2.1 is met by any one of: letting people turn the limit off, adjust it to at least ten times its length, or extend it at least ten times after a warning of 20 seconds or more. Limits of 20 hours or more need nothing.
  • In this demo any key press, click or typing in the form counts as activity and restarts the clock, so the warning only appears when you leave it alone. The demo clock and its controls are scaffolding, not part of the pattern.
  • On expiry the form is made inert and kept as it was. A real site saves the draft (on the server or in sessionStorage) before signing out, then restores it after sign-in.
  • Choosing Sign out is different from timing out: it clears the form, because someone leaving a shared computer does not want their words left behind.
  • Use Date.now() deadlines rather than counting ticks: timers slow down in background tabs, and the warning must still appear on time.

Sources: Understanding SC 2.2.1: Timing Adjustable · Understanding SC 2.2.5: Re-authenticating · WAI-ARIA Authoring Practices: Alert and Message Dialogs

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