Skip to main content

User testing

Help us improve this service. Take part in a short online exercise

Timeout modal

Experimental

A timeout warning that lets users extend the amount of time they have to complete a task.

<dialog class="ons-modal ons-js-modal ons-js-timeout-modal" id="dialog" role="dialog" aria-labelledby="ons-modal-title" data-redirect-url="#!" data-server-session-expires-at="" data-show-modal-time="58" data-server-session-expiry-endpoint="" data-countdown-text="To protect your information, your progress will be saved and you will be signed out in" data-countdown-expired-text="You are being signed out." data-minutes-text-singular="minute" data-minutes-text-plural="minutes" data-seconds-text-singular="second" data-seconds-text-plural="seconds" data-full-stop="true" aria-describedby="timeout-time-remaining">
  <div class="ons-modal__content">
    <h1 id="ons-modal-title" class="ons-modal__title">
      You will be signed out soon
    </h1>
    <div class="ons-modal__body">
      <p>It appears you have been inactive for a while.</p>
      <p class="ons-js-timeout-timer" aria-hidden="true" aria-relevant="additions"></p>
      <p class="ons-js-timeout-timer-acc ons-u-vh" role="status" id="timeout-time-remaining"></p>
    </div>
    <button type="submit" class="ons-btn ons-js-modal-btn ons-u-mt-s">
      <span class="ons-btn__inner">Continue survey</span>
    </button>
  </div>
</dialog>
{% from "components/timeout-modal/_macro.njk" import onsTimeoutModal %}

{{ onsTimeoutModal({
    "showModalTimeInSeconds": 58,
    "redirectUrl": "#!",
    "title": "You will be signed out soon",
    "textFirstLine": "It appears you have been inactive for a while.",
    "countdownText": "To protect your information, your progress will be saved and you will be signed out in",
    "countdownExpiredText": "You are being signed out.",
    "btnText": "Continue survey",
    "minutesTextSingular": "minute",
    "minutesTextPlural": "minutes",
    "secondsTextSingular": "second",
    "secondsTextPlural": "seconds",
    "endWithFullStop": true
}) }}
Name Type Required Description
showModalTimeInSeconds integer true Number of seconds the modal will be displayed
redirectUrl string true URL to redirect to when session times out
sessionExpiresAt string true Initial expiry time set by server on page load in ISO format
serverSessionExpiryEndpoint string true Endpoint to send requests to get or update expiry time
title string true Title text for the modal, used by aria-labelledby
textFirstLine string true Text content for modal e.g. ‘You’ve been inactive for a while’
countdownText string true Text content to accompany timer e.g. ‘You will be signed out in’
countdownExpiredText string true Text displayed as countdown expires e.g. ‘You are being signed out’
btnText string true Text displayed in button to reset session expiry time e.g. ‘Continue survey’
minutesTextSingular string true Text displayed in timer when minutes left is 1 e.g. ‘minute’
minutesTextPlural string true Text displayed in timer when minutes left is more than 1 e.g. ‘minutes’
secondsTextSingular string true Text displayed in timer when seconds left is 1 e.g. ‘second’
secondsTextPlural string true Text displayed in timer when seconds left is more than 1 e.g. ‘seconds’
endWithFullStop boolean false If set to true will end countdown text with full stop
{% from "components/modal/_macro.njk" import onsModal %}
{% macro onsTimeoutModal(params) %}
    {% call onsModal({
            "title": params.title,
            "btnText": params.btnText,
            "classes": "ons-js-timeout-modal",
            "attributes": {
                "data-redirect-url": params.redirectUrl,
                "data-server-session-expires-at": params.sessionExpiresAt,
                "data-show-modal-time": params.showModalTimeInSeconds,
                "data-server-session-expiry-endpoint": params.serverSessionExpiryEndpoint,
                "data-countdown-text": params.countdownText,
                "data-countdown-expired-text": params.countdownExpiredText,
                "data-minutes-text-singular": params.minutesTextSingular,
                "data-minutes-text-plural": params.minutesTextPlural,
                "data-seconds-text-singular": params.secondsTextSingular,
                "data-seconds-text-plural": params.secondsTextPlural,
                "data-full-stop": params.endWithFullStop,
                "aria-describedby": "timeout-time-remaining"
            }
        })
    %}
        <p>{{ params.textFirstLine }}</p>
        <p class="ons-js-timeout-timer" aria-hidden="true" aria-relevant="additions"></p>
        <p class="ons-js-timeout-timer-acc ons-u-vh" role="status" id="timeout-time-remaining"></p>
    {% endcall %}
{% endmacro %}

When to use this component

Use this component when a service sets a session expiry time to protect the user’s information.

There is specific guidance on how to help users extend a session.

How to use this component

The content of the timeout modal is set using the parameters shown in the macro options table in the previous example.

You will also need to set the parameters:

  • showModalTimeInSeconds with the number of seconds the modal should show for before the session expiry time (this should be at least 60 seconds)
  • sessionExpiresAt with the current expiry time in ISO format on page load
  • serverSessionExpiryEndpoint with a relative url to the server’s API endpoint for the component to send requests to
  • redirectUrl with a url to the session timed out error page the user is directed to when the session has timed out

Server requirements

The timeout modal component requires an API endpoint that handles two request methods and returns a JSON object containing the key expires_at with a value for the session expiry time in ISO format.

1. A request to reset the expiry time

When a request using a PATCH method is received the session expiry time should be reset to the original session length from the current time, and the response should contain the new expiry time.

A request to reset the expiry time will be sent when:

  • the modal is closed by the user
  • the user interacts with the page in any way, for example, by moving the cursor

To efficiently handle the number of requests made when the user interacts with the page, the requests are “throttled” so only one is sent every minute, and others in the queue are cleared.

2. A request for the current expiry time

When a request using a GET method is received, the response should contain the current expiry time.

The request is made to check if the same session is being interacted with in another tab. If the expiry time has changed, the timeout will be restarted against the expiry time in the response.

A request to get the current expiry time will be sent when the following happens:

  • before the modal opens
  • at 20-second intervals while the modal is open

Failure responses from the server

If the response status does not have a successful 200 code, the component will handle the response as a session expiry and redirect the user to the url set in the redirectUrl parameter.

Accessibility

This component helps services meet WCAG 2.1 success criterion 2.2.1 (Timing Adjustable). This requires services to warn users before a timeout occurs and allow them to extend it.

This component and it’s accessibility features are based on the GDS timeout modal prototype and research  . We will be carrying out our own accessibility testing to validate it.

Accessibility requirements

The WCAG guidelines state:

  • there should be no limit to the number of times a user can extend their session
  • the timeout modal should announce the remaining time at appropriate intervals to assistive technology
  • the underlying page content should be made inert, and the available action on the modal must be put in focus, for example, the “Continue” button
  • when the modal is closed the previously focused element on the page must be put back in focus

Accessibility attributes on the component’s elements

Element Attribute and value Description
<dialog> aria-labelledby=”ons-modal-title” To make the contained heading be read out as the title of the modal
<dialog> role="dialog" Makes sure the modal is backwards compatible if the screen reader does not support the html element
<dialog> aria-describedby="timeout-time-remaining" Reads out the live timer at 15-second intervals
Countdown timer aria-hidden="true" Visible countdown timer is hidden from screen readers because it would be too interruptive
Countdown timer aria-relevant=”additions” A workaround for aria-hidden for unsupported JAWS screen reader versions 17 and 18
Hidden interval timer role="status" To make sure the hidden interval timer is read out for iOS
Hidden interval timer aria-live="polite" To make sure the hidden interval timer does not interrupt other content being read out

Help improve this pattern

Let us know how we could improve this pattern or share your user research findings. Discuss the ‘Timeout modal’ component on GitHub