Browser tab: no focus highlight on the page

Every key on the app's keyboard takes focus from the page for a moment and
gives it back, so the site's focus ring (a 2px blue outline on the search
box, and a brighter border) flashed with each letter. Outline rings are
turned off with the page's clean-up CSS, and the guest preload strips
outline, border and box-shadow from the site's own :focus / :focus-within /
:focus-visible rules, keeping anything else those rules do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 09:28:59 -04:00
co-authored by Claude Opus 5.5
parent 0f9c468b1e
commit e1f541d366
3 changed files with 76 additions and 6 deletions
+65
View File
@@ -167,6 +167,71 @@ document.addEventListener('focusout', (e) => {
}, 0);
}, true);
// ------------------------------------------------------------------ no focus highlight
/*
* Nothing on the page shows that it has focus. The app's keyboard is outside the page, so every key press
* takes focus from the page for a moment and gives it straight back: a site's focus style would flash
* with each letter — and on stage it is clutter anyway. Outline rings (the browser's own and any site's)
* go with one rule in browser-tab.js's CLEAN_CSS; this takes the rest — a border that brightens, a glow —
* out of the site's own :focus / :focus-within / :focus-visible rules, and leaves everything else those
* rules do: a menu that opens on focus still opens. Sites add rules as they go (CSS-in-JS, late
* stylesheets), so this runs again just before anything can take focus. Another origin's stylesheets
* cannot be read and keep theirs; the outline rule still covers their rings.
*/
const FOCUS_LOOK = /^(?:outline|box-shadow|border)(?:-|$)/;
const seenRules = new WeakSet();
// Split at commas that are not inside brackets or quotes: "a:hover, b:is(c, d)" -> 2; "x:1; y:f(a;b)" -> 2.
function splitTop(text, sep) {
const out = [];
let depth = 0, quote = '', start = 0;
for (let i = 0; i < text.length; i++) {
const c = text[i];
if (quote) { if (c === '\\') i++; else if (c === quote) quote = ''; continue; }
if (c === '"' || c === "'") quote = c;
else if (c === '(' || c === '[') depth++;
else if (c === ')' || c === ']') depth--;
else if (c === sep && depth === 0) { out.push(text.slice(start, i).trim()); start = i + 1; }
}
out.push(text.slice(start).trim());
return out.filter(Boolean);
}
// Styled BECAUSE it has focus. ":not(:focus)" styles a field at rest, so it does not count.
function focusDriven(sel) {
return /:focus(?:-within|-visible)?(?![\w-])/.test(sel.replace(/:not\((?:[^()]|\([^()]*\))*\)/g, ''));
}
function isLook(decl) { return FOCUS_LOOK.test(decl.slice(0, decl.indexOf(':')).trim().toLowerCase()); }
function stripFocusLook(list, owner) {
for (let i = 0; i < list.length; i++) {
const r = list[i];
if (r.cssRules && r.cssRules.length) stripFocusLook(r.cssRules, r); // @media, @supports, @layer, nesting
if (!r.selectorText || !r.style || seenRules.has(r)) continue;
seenRules.add(r);
const sels = splitTop(r.selectorText, ',');
const focus = sels.filter(focusDriven);
if (!focus.length) continue;
// Declarations as written (cssText), so a shorthand built on var() survives intact.
const decls = splitTop(r.style.cssText, ';');
const keep = decls.filter((d) => !isLook(d));
if (keep.length === decls.length) continue;
if (focus.length === sels.length) { r.style.cssText = keep.join('; '); continue; }
// Shared with selectors that are not about focus ("a:hover, a:focus"): those keep the whole rule, and
// the focus ones move to a copy without the look.
r.selectorText = sels.filter((s) => !focusDriven(s)).join(', ');
if (!keep.length) continue;
try { owner.insertRule(focus.join(', ') + ' { ' + keep.join('; ') + ' }', i + 1); i++; seenRules.add(list[i]); } catch (_) {}
}
}
function hideFocusLook() {
const sheets = [...document.styleSheets, ...(document.adoptedStyleSheets || [])];
for (const sh of sheets) { try { stripFocusLook(sh.cssRules, sh); } catch (_) {} } // another origin's: unreadable
}
window.addEventListener('DOMContentLoaded', hideFocusLook);
window.addEventListener('load', hideFocusLook);
window.addEventListener('pointerdown', hideFocusLook, true);
document.addEventListener('focusin', hideFocusLook, true);
// ------------------------------------------------------------------ activity + five fingers
let lastActivity = 0;
function activity() {