“While you were away…”: wraps any region (dashboard, feed, board), notices what changed while you were elsewhere (hidden tab or idle) and gets you back in context in 3 seconds. On return, a card summarizes the absence by tier (blockers, decisions, updates, each with an icon and a label), and every entry leads to the element it concerns; “Replay (3s)” plays the changes back in order: the old content reappears in place, then melts into the current one with a light sweep and a “42 minutes ago → now” tag, while off-screen elements get edge arrows. “Unseen” markers stay on each element until it has actually been looked at.
Inactivity (no pointer, keyboard, scroll or focus) after which the user counts as away. 0 turns idle detection off (a hidden tab is still detected). Watching starts quietly after 10s of calm so nothing is missed, and is dropped at the first gesture.
threshold
number (ms)
20000
Minimum length of an absence for it to count: below it, changes are ignored (notes are kept).
follow
boolean
false
During the replay, scrolls to each off-screen element instead of showing an edge arrow.
Watched attributes (comma- or space-separated). “aria-*” covers the common ARIA states; add “class” explicitly. Text and added/removed nodes are always watched.
label
string
While you were away
Title of the return card.
dwell
number (ms)
1000
Time on screen (at least 60% of the element, or half a screen of it) before an element counts as seen. Counting starts 1.5s after the return.
data-away-label
string
On an element inside the region: makes it a “change unit” with this label. Otherwise: the nearest ancestor among li, tr, article, figure, fieldset or a titled section (label: its heading, aria-label or the start of its text).
data-away-tier
blocker | decision | done | info
Tier of the changes detected in this element, read at return time (the app can set it along with the change). Default: info.
data-away-key
boolean
Marks the part(s) whose text sums up the unit (a status, a value): the “before → after” uses only them.
data-away-ignore
boolean
Subtree that is never observed (clocks, decorative counters…).
Properties
Name
Type
Description
changes
{ id, kind, label, tier, time, seen, element, before, after }[]
Pending changes, sorted by importance then chronologically (read-only). before / after: summarized text before and after.
isAway
boolean
True during a confirmed absence.
Methods
Name
Description
away({ since? })
Starts an absence now (snapshot of the region, observation). since backdates the start: change times are then spread over the simulated span (demos, tests).
back()
Ends the absence and shows the card if anything changed. Returns the new changes.
note({ label, tier?, target?, detail?, time? })
Semantic event pushed by the app (“Deployment is waiting for your approval”). tier: blocker | decision | done | info; target: element or selector the entry leads to. During an absence the note waits for the return; otherwise a blocker opens the card right away, other tiers join the open card or the next return.
replay()
Replays the changes in ~3s (calling again stops it). Reduced motion: shows the before / after list.
markSeen(change?)
Marks one change (object or id) as seen; with no argument, everything, and closes the card.
Changes were seen (on screen, by click, by the replay or markSeen). detail: { changes, remaining }.
Content structure
Name
Description
(content)
Any region. The card is inserted at the top (sticky: it stays visible in a tall region and folds to its header once stuck); markers and the replay are drawn in an overlay layer, without touching the app's DOM.
Entry (button) and its tier chip (icon + label). data-tier, data-seen.
mv-while-away-mark / -dot
“Unseen” halo and badge set on a changed element.
mv-while-away-flash / -stamp / -edge / -ghost
Replay: halo and sweep, time tag, edge arrow, ghost of the old content.
mv-while-away-diffs
Before / after list (reduced motion).
CSS variables
Name
Default
Description
--mv-while-away-offset
var(--mv-space-3)
Distance from the top of the screen when the card is stuck (height of a fixed header, for example).
--mv-while-away-bg
var(--mv-surface-raised)
Card background.
--mv-while-away-blocker
var(--mv-danger)
Blocker tier color.
--mv-while-away-decision
var(--mv-warning)
Decision tier color.
--mv-while-away-done
var(--mv-success)
Done tier color.
--mv-while-away-info
var(--mv-info)
Update tier color.
Accessibility
The card is a named region (“While you were away”), announced through a polite live region (“While you were away (47 min): 1 blocker, 2 decisions, 5 updates.”); focus moves to it only after a tab switch, never after mere idling nor while you are typing. Entries are real buttons (role=list), sorted by importance, navigable with the arrows, Home and End; each one spells out the tier, the label, “Before: … Now: …”, its age and a “Seen / Unseen” state; activating it scrolls to the element, focuses it and marks it as seen. The replay is purely visual (aria-hidden) and announced when it starts and ends; Escape stops it. Markers, halos and arrows are aria-hidden, and a tier color is always paired with an icon and a label. Reduced motion (system or data-motion="reduce"): no animation, “Replay” becomes “Before / after” (aria-expanded) and opens the comparison list, markers are static. Performance: a single MutationObserver, active only during an absence, batched with no layout reads; the snapshot is cloned once at the start of the absence; one IntersectionObserver for “seen”; overlays recomputed at most once per frame (read then write); everything is released when the element is removed. Password fields are never copied.