Last active
July 31, 2025 20:34
-
-
Save gyf304/798aaf87e571afe247bd5c595fe7e07e to your computer and use it in GitHub Desktop.
Dark Mode for all Websites
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name Darkify | |
| // @namespace http://yifangu.com | |
| // @license MIT | |
| // @version 2024-02-27 | |
| // @description Selectively toggle dark mode for any website | |
| // @author You | |
| // @match *://*/* | |
| // @run-at document-start | |
| // @inject-into content | |
| // @grant GM.setValue | |
| // @grant GM.getValue | |
| // @grant GM.info | |
| // ==/UserScript== | |
| (async function () { | |
| "use strict"; | |
| if (window.top !== window.self) { | |
| return; | |
| } | |
| const ver = GM.info.script.version; | |
| const firstRunReadme = [ | |
| "This is your first time running Darkify", | |
| "Press CTRL+SHIFT+D to toggle on/off dark mode at a given site.", | |
| "For mobile devices, hold down three fingers for 3 seconds to toggle on/off dark mode.", | |
| `Version ${ver}`, | |
| ].join("\n"); | |
| const upgradeReadme = [ | |
| `Darkify has been upgraded to version ${ver}` | |
| ].join("\n"); | |
| const defaultConfig = { | |
| enabled: false, | |
| }; | |
| const savedVer = await GM.getValue("version"); | |
| if (savedVer === undefined) { | |
| // first run | |
| alert(firstRunReadme); | |
| await GM.setValue("version", ver); | |
| } else if (savedVer !== ver) { | |
| alert(upgradeReadme); | |
| await GM.setValue("version", ver); | |
| } | |
| function hex(s) { | |
| return s.split("").map((c) => c.charCodeAt(0).toString(16)).join(""); | |
| } | |
| const loadConfig = async () => { | |
| const siteKey = location.hostname.toLowerCase().replaceAll(/[^a-z]/g, "_"); | |
| const configJson = await GM.getValue(`siteConfig.${siteKey}`, "{}"); | |
| let config = defaultConfig; | |
| try { | |
| config = { ...defaultConfig, ...JSON.parse(configJson) }; | |
| } catch (e) { | |
| // ignore | |
| } | |
| return config; | |
| }; | |
| const saveConfig = async (config) => { | |
| const siteKey = location.hostname.toLowerCase().replaceAll(/[^a-z]/g, "_"); | |
| await GM.setValue(`siteConfig.${siteKey}`, JSON.stringify(config)); | |
| } | |
| const config = await loadConfig(); | |
| const { enabled } = config; | |
| const toggle = async () => { | |
| await saveConfig({ ...config, enabled: !enabled }); | |
| location.reload(); | |
| }; | |
| window.addEventListener("keydown", async (e) => { | |
| // ctrl + shift + d | |
| if (e.ctrlKey && e.shiftKey && e.key === "D") { | |
| e.preventDefault(); | |
| await toggle(); | |
| } | |
| }); | |
| let toggleTimeoutHandle; | |
| document.addEventListener("touchmove", (e) => { | |
| const num = e.touches.length; | |
| if (num === 3) { | |
| if (toggleTimeoutHandle === undefined) { | |
| toggleTimeoutHandle = setTimeout(async () => { | |
| await toggle(); | |
| toggleTimeoutHandle = undefined; | |
| }, 3000); | |
| } | |
| } else { | |
| if (toggleTimeoutHandle !== undefined) { | |
| clearTimeout(toggleTimeoutHandle); | |
| toggleTimeoutHandle = undefined; | |
| } | |
| } | |
| }); | |
| document.addEventListener("touchend", () => { | |
| if (toggleTimeoutHandle !== undefined) { | |
| clearTimeout(toggleTimeoutHandle); | |
| toggleTimeoutHandle = undefined; | |
| } | |
| }); | |
| if (!enabled) { | |
| return; | |
| } | |
| const css = ` | |
| :root { | |
| color-scheme: dark; | |
| background-color: white; | |
| filter: invert(100%) hue-rotate(180deg) !important; | |
| -webkit-font-smoothing: antialiased; | |
| color: black; | |
| } | |
| body { | |
| color-scheme: light; | |
| background-color: #000 !important; | |
| background: linear-gradient(#fff, #fff); | |
| } | |
| picture>source,img:not([src*="svg"]),video,iframe,embed,object,canvas,.logo,.darkify-ignore { | |
| filter: brightness(80%) invert(100%) hue-rotate(180deg) !important; | |
| } | |
| img[src*="svg"]:hover { | |
| background-color: #aaa !important; | |
| } | |
| `; | |
| function darkify() { | |
| if (document.getElementById("darkify")) { | |
| return; | |
| } | |
| const style = document.createElement("style"); | |
| style.id = "darkify"; | |
| style.innerHTML = css; | |
| const themeColorMeta = document.createElement("meta"); | |
| themeColorMeta.name = "theme-color"; | |
| themeColorMeta.content = "#000000"; | |
| document.head.appendChild(style); | |
| document.head.appendChild(themeColorMeta); | |
| document.addEventListener("DOMContentLoaded", () => { | |
| const divs = document.querySelectorAll("div"); | |
| for (const div of divs) { | |
| if (!div.classList.contains("darkify-ignore")) { | |
| continue; | |
| } | |
| const cs = getComputedStyle(div); | |
| const bg = cs.backgroundImage; | |
| const bs = cs.backgroundSize; | |
| if (bg === "none") { | |
| continue; | |
| } | |
| if (!["cover", "contain"].includes(bs)) { | |
| continue; | |
| } | |
| if (bg.includes(".svg") || bg.includes(".gif")) { | |
| continue; | |
| } | |
| div.classList.add("darkify-ignore"); | |
| } | |
| }); | |
| } | |
| darkify(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment