Help me fix this dark mode script?
So, I tried writing a code that adds a dark mode to this google page that lacks it. The way I access this page is through another script that lets me switch gmail account without opening new tabs. The only way I managed to make it work is through this login page.
The issue I'm having is that the page does not load the dark mode as soon as it is opened and needs a refresh in order to work. This is the case most of the times, as sometimes it will just randomly work. I'm not sure why that's the case.
Here is the script:
// ==UserScript==
// u/nameGoogle Accounts – True Dark mode (recolor)
// u/namespacehttps://accounts.google.com/
// u/version3.6
// u/description True dark mode: replaces light backgrounds and dark text, preserves colors (logos/avatars). Fixes Safari bfcache + Shadow DOM.
// u/authorLorenzo
// u/matchhttps://mail.google.com/*
// u/matchhttps://accounts.google.com/*
// u/grantnone
// u/run-atdocument-start
// ==/UserScript==
(function () {
"use strict";
const BG_DARK = "#202124"; // page background (official Google dark mode color)
const CARD_DARK = "#292a2d"; // card background, slightly lighter to distinguish panels
const TEXT_LIGHT = "#e8eaed"; // primary text
const TEXT_MUTED = "#9aa0a6"; // secondary text (email below the name)
const BORDER_DARK = "#3c4043"; // dividers/borders
console.log("[GADM] script active on:", location.href);
function parseRgb(str) {
const m = str && str.match(/rgba?\(([^)]+)\)/);
if (!m) return null;
const parts = m[1].split(",").map((s) => parseFloat(s));
const [r, g, b, a = 1] = parts;
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;
return { r, g, b, a };
}
function isGrayish(r, g, b, tolerance = 15) {
return Math.max(r, g, b) - Math.min(r, g, b) <= tolerance;
}
function recolor(el) {
if (!el || el.nodeType !== 1) return;
const tag = el.tagName;
if (tag === "IMG" || tag === "SVG" || tag === "PATH" || tag === "SCRIPT" || tag === "STYLE") return;
if (el.closest && el.closest("svg")) return;
const cs = getComputedStyle(el);
// Background: only if light gray/white, never if colored (avatar)
const bg = parseRgb(cs.backgroundColor);
if (bg && bg.a > 0.05 && isGrayish(bg.r, bg.g, bg.b) && (bg.r + bg.g + bg.b) / 3 > 190) {
el.style.setProperty("background-color", CARD_DARK, "important");
}
// Text: dark/black -> light; medium gray -> muted light gray
const col = parseRgb(cs.color);
if (col && isGrayish(col.r, col.g, col.b, 25)) {
const bright = (col.r + col.g + col.b) / 3;
if (bright < 90) {
el.style.setProperty("color", TEXT_LIGHT, "important");
} else if (bright < 190) {
el.style.setProperty("color", TEXT_MUTED, "important");
}
}
// Light borders -> dark borders
["borderTopColor", "borderRightColor", "borderBottomColor", "borderLeftColor"].forEach((prop) => {
const bc = parseRgb(cs[prop]);
if (bc && bc.a > 0.05 && isGrayish(bc.r, bc.g, bc.b) && (bc.r + bc.g + bc.b) / 3 > 190) {
const cssProp = prop.replace(/([A-Z])/g, "-$1").toLowerCase();
el.style.setProperty(cssProp, BORDER_DARK, "important");
}
});
}
// Traverses the DOM deeply, entering shadow roots as well
function forEachDeep(root, fn) {
if (!root || !root.querySelectorAll) return;
fn(root);
root.querySelectorAll("*").forEach((el) => {
fn(el);
if (el.shadowRoot) {
forEachDeep(el.shadowRoot, fn);
}
});
}
function recolorAll(root) {
forEachDeep(root, recolor);
}
// Observes a root (document or shadow root) for new nodes
function observeRoot(root) {
new MutationObserver((mutations) => {
mutations.forEach((m) => {
m.addedNodes.forEach((node) => {
if (node.nodeType === 1) recolorAll(node);
});
});
}).observe(root, { childList: true, subtree: true });
}
// Intercepts the creation of every shadow root, so we can
// recolor and observe it at the exact moment it is created
const origAttachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function (init) {
const shadow = origAttachShadow.call(this, init);
observeRoot(shadow);
// Recolor after a brief delay to allow content time to populate
setTimeout(() => recolorAll(shadow), 0);
return shadow;
};
function init() {
document.documentElement.style.setProperty("background-color", BG_DARK, "important");
if (document.body) {
document.body.style.setProperty("background-color", BG_DARK, "important");
}
recolorAll(document.body || document.documentElement);
console.log("[GADM] recolor applied");
}
init();
document.addEventListener("DOMContentLoaded", init);
// Fix for restoring from cache (bfcache) on Safari
window.addEventListener("pageshow", (event) => {
console.log("[GADM] pageshow, persisted:", event.persisted);
init();
});
observeRoot(document.documentElement);
// Periodic fallback: captures style changes without new nodes (e.g., hover/focus)
setInterval(() => recolorAll(document.body || document.documentElement), 1500);
})();
Is anyone able to tell me why does this only sometime work correctly?