Session Warning Dialog
import { SessionWarningDialog } from '@absoluteui/components/session-warning-dialog';import '@absoluteui/components/session-warning-dialog/style.css';Using Angular? See Session Warning Dialog for Angular.
What it does
Section titled “What it does”An alert dialog that warns before a session ends (WCAG 2.2.1 Timing Adjustable). It provides:
- a countdown to a deadline given as an
Instant, recomputed from the clock on every tick (timezone independent); - “Stay signed in” (initial focus) and “Sign out” actions, and an expired state with “Sign in again”;
- throttled announcements: at multiples of
announceInterval, then at 60, 30, and 10 seconds, not every second; - optional automatic opening
warnBeforethe deadline; - events for every outcome:
aui-extend,aui-sign-out,aui-expire.
It pauses, extends, and ends nothing on its own. Your application calls its session service when the events fire. AbsoluteUI is not an authentication provider.
When to use
Section titled “When to use”- Warning users before an inactivity or absolute session timeout.
- Any deadline where the user must be able to extend with one simple action.
When not to use
Section titled “When not to use”- Non-urgent notices: use Toast or Alert.
- Enforcing the timeout: the server owns session expiry.
Examples
Section titled “Examples”Warn two minutes before expiry and extend on request
Section titled “Warn two minutes before expiry and extend on request”import { duration, instant } from '@absoluteui/core';import { SessionWarningDialog } from '@absoluteui/components/session-warning-dialog';
const warning = SessionWarningDialog.create({ expiresAt: instant(Date.now() + 15 * 60_000), warnBefore: duration(2 * 60_000) });warning.element.addEventListener('aui-extend', () => { void fetch('/api/session/refresh', { method: 'POST' }).then(async (r) => { const body: unknown = await r.json(); if (typeof body === 'object' && body !== null && 'expiresAt' in body && typeof body.expiresAt === 'number') { warning.update({ expiresAt: instant(body.expiresAt) }); } });});warning.element.addEventListener('aui-sign-out', () => { location.assign('/sign-out');});Open manually with custom texts
Section titled “Open manually with custom texts”import { instant } from '@absoluteui/core';import { SessionWarningDialog } from '@absoluteui/components/session-warning-dialog';
const warning = SessionWarningDialog.create({ expiresAt: instant(Date.parse('2026-03-08T12:00:00Z')), title: 'Still working?', message: 'Unsaved changes are kept for 10 minutes after sign-out.',});warning.open();import { duration, instant } from '@absoluteui/core';import { SessionWarningDialog } from '@absoluteui/components/session-warning-dialog';import '@absoluteui/components/button/style.css';import '@absoluteui/components/dialog/style.css';import '@absoluteui/components/session-warning-dialog/style.css';
const warning = SessionWarningDialog.create({ expiresAt: instant(Date.now() + 30 * 60_000), warnBefore: duration(5 * 60_000) });warning.element.addEventListener('aui-extend', () => { void fetch('/api/session/refresh', { method: 'POST' }).then(() => { warning.update({ expiresAt: instant(Date.now() + 30 * 60_000) }); });});warning.element.addEventListener('aui-expire', () => { location.assign('/signed-out');});More usage
Section titled “More usage”import { duration, instant } from '@absoluteui/core';import { SessionWarningDialog } from '@absoluteui/components/session-warning-dialog';
const warning = SessionWarningDialog.create({ expiresAt: instant(Date.now() + 15 * 60_000), warnBefore: duration(120_000) });warning.element.addEventListener('aui-sign-out', () => { location.assign('/sign-out');});Anatomy
Section titled “Anatomy”<dialog class="aui-dialog aui-dialog--sm aui-session-warning-dialog" role="alertdialog" aria-labelledby="T" aria-describedby="M C"> <header class="aui-dialog__header"><h2 id="T">Your session is about to expire</h2></header> <div class="aui-dialog__body"> <div class="aui-session-warning"> <p class="aui-session-warning__message" id="M">You will be signed out soon ...</p> <p class="aui-session-warning__countdown" id="C">Time remaining: 1 minute, 30 seconds</p> <div class="aui-session-warning__content">(optional content)</div> </div> </div> <footer class="aui-dialog__footer"> <button class="aui-button aui-button--primary" autofocus>Stay signed in</button> <button class="aui-button aui-button--secondary">Sign out</button> </footer></dialog>API reference
Section titled “API reference”Factory
Section titled “Factory”SessionWarningDialog.create(options: SessionWarningDialogOptions, doc?: Document): SessionWarningDialogGlobal configuration key: sessionWarningDialog (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
expiresAt |
Instant |
When the session ends. Required. | |
warnBefore |
Duration |
Open automatically this long before expiresAt (at least 20 s). | |
title |
string |
Default: localized “Your session is about to expire”. | |
message |
string |
Default: localized inactivity message. | |
content |
Node | string |
Additional body content below the countdown. | |
extendLabel |
string |
Default: localized “Stay signed in”. | |
signOutLabel |
string |
Default: localized “Sign out”. | |
expiredTitle |
string |
Default: localized “Your session has expired”. | |
expiredMessage |
string |
Default: localized “You have been signed out. Sign in again to continue.” | |
signInLabel |
string |
The single action after expiry. Default: localized “Sign in again”. | |
announceInterval |
number |
60 |
Announce the remaining time each time it crosses a multiple of this many seconds (15..3600), and at 60, 30, and 10 seconds. Globally configurable. |
escapeExtends |
boolean |
true |
Escape means “Stay signed in”; when false, Escape does nothing. Globally configurable. |
locale |
string |
"en-US" |
Message and unit-formatting locale. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
dialog |
Dialog |
The underlying Dialog. |
element |
HTMLDialogElement |
The <dialog>; events are dispatched on it. |
isOpen |
boolean |
Whether the dialog is open. |
remaining |
Duration |
Time left (never negative). |
expired |
boolean |
The deadline has passed. |
options |
Readonly<SessionWarningDialogOptions> |
Current options. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
open(): void |
Open now; focus moves to “Stay signed in”. |
close(): void |
Close programmatically (no events besides aui-close). |
update(options: Partial<SessionWarningDialogOptions>): void |
Merge options; a new expiresAt re-arms auto-open and expiry. |
destroy(): void |
Stop timers and remove the dialog. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-open |
null |
No | No | The dialog opened (from Dialog). |
aui-close |
{ returnValue: string; reason: DialogCloseReason } |
No | No | The dialog closed (from Dialog). |
aui-extend |
{ reason: 'action' | 'escape' } |
No | No | The user chose to stay signed in: extend the session, then call update({ expiresAt }). |
aui-sign-out |
{ reason: 'action' | 'expired' } |
No | No | The user chose to sign out, or acknowledged expiry (“Sign in again”). |
aui-expire |
null |
No | No | The deadline passed (once per deadline, open or closed). |
Content areas
Section titled “Content areas”| Area | Description |
|---|---|
content |
Optional Node or text below the countdown (Angular: the template content). |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-session-warning-dialog |
The <dialog> (also aui-dialog). |
aui-session-warning |
Body wrapper. |
aui-session-warning__message |
Message. |
aui-session-warning__countdown |
Countdown text. |
aui-session-warning__content |
Consumer content. |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-color-text | --aui-font-family-sans | --aui-font-size-md | --aui-font-weight-semibold | --aui-line-height-normal | --aui-space-3
TypeScript definitions
Section titled “TypeScript definitions”interface SessionWarningDialogConfig { announceInterval: number; escapeExtends: boolean }interface SessionWarningDialogOptions extends Partial<SessionWarningDialogConfig> { expiresAt: Instant; warnBefore?: Duration; title?: string; message?: string; content?: Node | string; extendLabel?: string; signOutLabel?: string; expiredTitle?: string; expiredMessage?: string; signInLabel?: string; locale?: string;}interface SessionWarningExtendDetail { reason: 'action' | 'escape' }interface SessionWarningSignOutDetail { reason: 'action' | 'expired' }function formatRemaining(seconds: number, locale: string): string;Accessibility
Section titled “Accessibility”Pattern: WAI-ARIA alertdialog (native modal <dialog>) named by its title and described by the message and countdown.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Tab / Shift+Tab | Dialog open | Moves between the actions (focus is contained by the modal). |
| Enter / Space | Action focused | Stay signed in, Sign out, or Sign in again. |
| Escape | Dialog open, not expired | Stay signed in (escapeExtends: true, default); nothing when false. |
| Element | Role |
|---|---|
| dialog | alertdialog |
| actions | button (native) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-labelledby |
dialog | Title id |
aria-describedby |
dialog | Message and countdown ids (message only when expired) |
On open, focus moves to “Stay signed in” (least destructive). On expiry, to “Sign in again”. On close, focus returns to the element that had it.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”| Criterion | Level | Note |
|---|---|---|
| 1.3.1 Info and Relationships | A | |
| 1.4.3 Contrast (Minimum) | AA | |
| 2.1.1 Keyboard | A | |
| 2.1.2 No Keyboard Trap | A | |
| 2.2.1 Timing Adjustable | A | |
| 2.4.3 Focus Order | A | |
| 2.4.7 Focus Visible | AA | |
| 4.1.2 Name, Role, Value | A | |
| 4.1.3 Status Messages | AA |
Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- WCAG 2.2.1: users are warned and can extend with one simple action; warnBefore must be at least 20 s.
- Opening an alertdialog is read by screen readers; afterwards the remaining time is announced politely only when it crosses a multiple of announceInterval and at 60, 30, and 10 seconds. Expiry is announced assertively.
- Announcements go to a live region inside the modal (everything outside is inert).
- Manual screen-reader verification (NVDA, JAWS, VoiceOver) is pending.
Behavior
Section titled “Behavior”States
Section titled “States”closed (scheduled when warnBefore is set), open and counting down, expired, suspended (answered; waits for a new expiresAt).
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| closed | open(), or expiresAt - warnBefore reached |
open; focus on “Stay signed in” |
| open | “Stay signed in” or Escape | closed, suspended; aui-extend |
| open | “Sign out” | closed, suspended; aui-sign-out (action) |
| open | deadline | expired: title, message, and a single “Sign in again”; announced assertively; aui-expire |
| expired | “Sign in again” | closed; aui-sign-out (expired) |
| closed | deadline | aui-expire (stays closed) |
| suspended | update({ expiresAt }) |
scheduled again |
Accessibility notes
Section titled “Accessibility notes”- WCAG 2.2.1: the user is warned and can extend with one action;
warnBeforemust be at least 20 seconds. - Screen readers read the alert dialog when it opens. Afterwards the remaining time is announced politely only when it crosses a multiple of
announceIntervaland at 60, 30, and 10 seconds; expiry is announced assertively. Announcements go to a live region inside the modal. - No animation of its own; the countdown is a text change once per second. Dialog transitions follow reduced motion.
- Manual screen-reader verification is pending.
Responsive behavior
Section titled “Responsive behavior”Dialog size sm; actions wrap on narrow screens.
Right-to-left
Section titled “Right-to-left”As Dialog (logical properties). The countdown text is localized.
Theming
Section titled “Theming”Dialog surface and Button variants; the countdown uses color-text with tabular numbers.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { sessionWarningDialog: { announceInterval: 120, escapeExtends: false } } });Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options are dynamic.
Timezones
Section titled “Timezones”Timezone independent. expiresAt is an exact Instant; remaining time is expiresAt - Date.now(), a duration. The configured timezone and the device timezone are never used. Only the device clock matters: derive expiresAt from your server’s expiry (for example “expires in N seconds” measured on receipt) to limit clock skew.
None of its own; Dialog has no close button here.
Security
Section titled “Security”- Never signs out, extends, or stores tokens: it emits events.
- All text is rendered with
textContent;contentstrings are text. - Extending must happen on the server; a client-only extension changes nothing.
- CSP-safe.
Data handling
Section titled “Data handling”Holds only the deadline and texts in memory. No persistence, network access, or telemetry.
Performance
Section titled “Performance”One timer at a time: a tick aligned to the next whole second while open, and one scheduling timer while closed (capped at 2^31 - 1 ms and re-armed). Budget: open() < 16 ms (e2e performance suite).
Supported data scale
Section titled “Supported data scale”One dialog per page.
Edge cases
Section titled “Edge cases”- Background tabs throttle timers; the next tick recomputes from the clock, so the countdown is correct but may update late, and missed thresholds are announced once.
- A deadline already past at creation opens in the expired state without
aui-expire. - After the user answers, auto-open waits for
update({ expiresAt }), so an unchanged deadline does not reopen the dialog in a loop. update({ expiresAt })while expired returns to the countdown.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
SESSION_WARNING_DIALOG_OPTIONS |
configuration | expiresAt not an Instant, warnBefore not a Duration of at least 20 s, empty texts, invalid content. |
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION |
configuration | Invalid or unknown configuration. |
A failed construction creates no dialog.
Known limitations
Section titled “Known limitations”- The device clock is trusted.
- Escape extends by default.
- Manual screen-reader verification is pending.
Unsupported scenarios
Section titled “Unsupported scenarios”- Activity tracking (the application decides when the session was used).
- Cross-tab coordination (use
BroadcastChannelin the application and callupdate()).