Idle Detector
stimeo--idle
Tells you when interaction stops, warns before it does, and reports when you come back.
Reports when interaction with the page stops. It watches pointer movement, keys, scrolling, and returning to the tab. After the time you set with none of that, it reports the idle state. It can also warn shortly before, so you can confirm with someone before their session ends. When interaction returns, that is reported immediately too. It brings no warning dialog of its own, so pair it with one. It does not touch your server session. Normally one of these sits on the page as a whole.
Stop interacting with the page: a warning shows after 3s and the idle state after 6s. Move the mouse or press a key to come back.
Keyboard
This component has no keyboard interactions of its own.
<%# Idle / session-timeout demo: the controller watches document-level activity and,
after a short demo timeout, fires prompt -> idle; interacting again fires active.
Real apps use a ~15-minute timeout; this demo shortens it (6s, warning at 3s) so the
transitions are visible. The library ships no UI, so demo.js mirrors the events into
the status badge below (whose labels are owned here for i18n). %>
<div
class="idle-demo"
data-controller="stimeo--idle"
data-stimeo--idle-timeout-value="6000"
data-stimeo--idle-prompt-before-value="3000">
<p class="idle-demo__hint"><%= t("components.idle.demo.hint") %></p>
<p
class="idle-demo__status"
role="status"
aria-live="polite"
data-idle-demo-status
data-label-active="<%= t("components.idle.demo.active") %>"
data-label-prompt="<%= t("components.idle.demo.prompt") %>"
data-label-idle="<%= t("components.idle.demo.idle") %>"></p>
</div>
/*
* Presentation-only styles for the idle demo. The library only fires events and toggles
* data-idle; this CSS lays out the hint + status badge and colors the badge by the
* demo-state mirror that demo.js writes (active / prompt / idle).
*/
.idle-demo {
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 28rem;
}
.idle-demo__hint {
margin: 0;
color: var(--color-text-muted);
}
.idle-demo__status {
margin: 0;
padding: 0.5rem 0.75rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
font-weight: 600;
text-align: center;
}
.idle-demo[data-demo-state="prompt"] .idle-demo__status {
border-color: var(--amber-500);
background: var(--amber-50);
color: var(--amber-500);
}
.idle-demo[data-demo-state="idle"] .idle-demo__status {
border-color: var(--danger-500);
background: var(--danger-50);
color: var(--color-accent);
}
// Idle demo (consumer-side JS).
//
// A ~15-minute production timeout can't be shown in a catalog, so the markup uses a
// short timeout / prompt window. The controller fires prompt -> idle as the page sits
// untouched and active when you interact again; this JS only mirrors those events into
// a visible status badge (the library ships no UI of its own). The badge copy is read
// from data-label-* so it stays owned by the i18n'd ERB.
document.querySelectorAll(".idle-demo").forEach((root) => {
const badge = root.querySelector("[data-idle-demo-status]");
if (!badge) return;
const labels = {
active: badge.dataset.labelActive,
prompt: badge.dataset.labelPrompt,
idle: badge.dataset.labelIdle,
};
const show = (state) => {
root.dataset.demoState = state;
badge.textContent = labels[state];
};
show("active");
root.addEventListener("stimeo--idle:prompt", () => show("prompt"));
root.addEventListener("stimeo--idle:idle", () => show("idle"));
root.addEventListener("stimeo--idle:active", () => show("active"));
});
These demo styles use shared design tokens (light + dark). Copy the shared styles too, then toggle data-theme on your root element for dark mode.
The data-* attributes you add to your own HTML to wire this component. Put the data-controller below on a root element, then place its targets / values / actions inside that element.
On the root element
data-controller="stimeo--idle"
Values
| Name | Description | Attribute |
|---|---|---|
timeout
|
Milliseconds of no activity before idle (default 900000). | data-stimeo--idle-timeout-value |
promptBefore
|
Milliseconds before the timeout to fire prompt (0 = no prompt). |
data-stimeo--idle-prompt-before-value |
events
|
Activity event types that reset the clock, watched passively; visibilitychange is always watched too. |
data-stimeo--idle-events-value |
Events
| Name | Description | Event |
|---|---|---|
prompt
|
Fires promptBefore ms before the timeout, with detail.remaining (ms). |
stimeo--idle:prompt |
idle
|
Fires when the timeout is reached. | stimeo--idle:idle |
active
|
Fires when the user interacts again after a prompt or idle. | stimeo--idle:active |
State hooks
The library only manages these ARIA/data attributes and custom properties. Your CSS reads them to render the look — selectors like [aria-selected], [aria-expanded], or var(--stimeo--…) hook into this state.
| Hook | Target | Meaning |
|---|---|---|
data-idle |
Controller element | Present (set to true) while idle; removed when activity resumes. |