TIL: Dim too-bright pages with a Tampermonkey overlay
I run macOS, every app, and every website in dark mode. But plenty of sites ignore prefers-color-scheme and stay blinding white — or ship a dark theme that still leaves one giant white panel. So I wrote a Tampermonkey userscript that drops a translucent dark layer over near-white pages, with macOS-style hotkeys to nudge the brightness. The whole thing is one trick: a fixed, full-viewport <div> floating on top of everything.
The overlay. It’s a position: fixed div covering 100% × 100% at a huge z-index, tinted with a semi-transparent color. The key line is pointer-events: none — clicks, scroll, and text selection pass straight through, so you’re looking through tinted glass, not covering the page with a click-blocker:
overlay.style.cssText = `
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(15, 8, 0, 0.16); // 👉 a hair of warm yellow, not neutral gray
pointer-events: none; // 👉 the whole reason this works
z-index: 99999999;
`;
The tint is 15, 8, 0 — a touch of warm yellow rather than flat gray, which reads softer at night. Swap OVERLAY_RGB to '0, 0, 0' for plain gray.
Only dim when it makes sense. In auto mode the overlay appears only when both conditions hold: the system is in dark mode, and the page background is actually near-white. It samples body, documentElement, and main, parses the computed backgroundColor, and calls it white when every channel clears 240 — so a page that’s already dark is left alone:
function run() {
if (userControlled) { /* manual wins, see below */ }
const isDarkMode = darkModeQuery.matches; // matchMedia('(prefers-color-scheme: dark)')
const isWhitePage = isBackgroundWhite(); // r,g,b all > 240
toggleOverlay(isDarkMode && isWhitePage);
}
Manual control wins and sticks. The moment I press a hotkey, the script flips a userControlled flag and writes the opacity (and on/off state) to localStorage, which is scoped per origin. On the next load, a stored value short-circuits detection entirely — that site stays exactly where I left it, forever:
const stored = localStorage.getItem(STORAGE_KEY);
if (stored !== null) {
opacity = clamp(parseFloat(stored));
userControlled = true; // 👉 from now on, ignore auto-detection on this site
}
Hotkeys + HUD. ctrl+alt+cmd+F1 darkens, F2 lightens, F3 toggles on/off. The listener runs in the capture phase and calls preventDefault() so pages can’t swallow the combo. Each press flashes a macOS-style brightness HUD — a sun icon over a level bar — that fades after 2s. The bar tracks brightness, not opacity, so it’s inverted:
const level = enabled ? 1 - opacity / MAX : 1; // more dimming → emptier bar
Download make-white-pages-darker.user.js — open it in Tampermonkey to install, or paste it into a new userscript.
Because it re-runs on DOMContentLoaded and listens for theme changes (darkModeQuery.addEventListener('change', run)), flipping macOS between light and dark updates every open tab live.
Let's stay connected!
Author
I'm Oliver Nguyen. A software maker working mostly in Go and JavaScript. I enjoy learning and seeing a better version of myself each day. Occasionally spin off new open source projects. Share knowledge and thoughts during my journey.