A single-purpose static page that shows exactly what a web page can — and cannot — learn when you switch away from it. It runs the same focus and visibility check an online assessment or proctoring page runs, so you can see the record those pages build.
Live: https://autocue-interview-tools.github.io/detect-tab-focus/
Nothing is uploaded. The page is static, has no analytics, no cookies and no storage, and makes no network requests after it loads.
| Verdict | Meaning |
|---|---|
| Focused | Visible and holding keyboard focus — nothing to record |
| Blurred | Still visible, but focus moved to another window or application |
| Hidden | Not being rendered: another tab is in front, the window is minimised, or the screen is off |
Alongside the verdict it keeps a live event log — every visibilitychange, blur,
focus, pagehide, pageshow, freeze and resume, with a timestamp and the state each
one produced — plus separate counters for tab hides and focus losses, total and longest
time away, and a to-scale ribbon of the whole session.
A lockout overlay reproduces the full-screen warning many assessment platforms display the moment focus is lost. It is off by default.
Two readings, available in every browser with no permission and no way to disable them:
document.visibilityState—visibleorhidden. Switching tabs, minimising the window or locking the screen makes ithidden.document.hasFocus()— whether this document receives keystrokes. Clicking a second window or another application leaves the page visible but takes focus away.
Both are read after every relevant event, so the ordering of blur and visibilitychange
— which differs between browsers — cannot desynchronise the counters from the displayed
state. An absence that starts as a blur and turns into a tab switch is timed as one
continuous absence rather than two.
freeze and resume are Chromium-only and fire when the browser suspends a background tab
to save memory. On iOS, switching apps reports as hidden rather than blurred, because the
whole browser is backgrounded.
Twelve fully translated static pages, each with its own metadata, canonical URL and
hreflang alternates:
English · Español · 简体中文 · हिन्दी · العربية (RTL) · Português · Русский · 日本語 · Deutsch · Français · 한국어 · Bahasa Indonesia
Pages are generated from one template and one JSON file per language.
node build.mjs # writes index.html, <lang>/index.html, sitemap.xml, robots.txt, 404.html
node verify.mjs # structural checks: hreflang, canonical, JSON-LD, i18n key coverage
To change copy, edit i18n/<code>.json and rebuild — never edit the generated HTML.
To add a language, add i18n/<code>.json and append its code to ORDER in build.mjs;
verify.mjs fails if a translation file is not listed.
The Open Graph image is rendered from src/og.html:
chrome --headless --screenshot=assets/og.png --window-size=1200,630 src/og.html
build.mjs generator
verify.mjs post-build checks
i18n/*.json one file per language: metadata, page copy, runtime strings
assets/style.css shared stylesheet
assets/app.js detection logic, reads window.__I18N__ for its strings
src/og.html source of the social card
detect-screen — what a browser can learn about the displays attached to your machine.
MIT