Last active
March 18, 2026 14:01
-
-
Save mattmc3/96ca5dd86a450702b8267afb67a30b47 to your computer and use it in GitHub Desktop.
TamperMonkey script to toggle dark mode
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 Simple Dark Mode Toggle | |
| // @namespace http://tampermonkey.net/ | |
| // @version 2.3 | |
| // @description Toggle dark mode on any site with UI pill toggle and keyboard shortcut | |
| // @match *://*/* | |
| // @grant GM_registerMenuCommand | |
| // @grant GM_unregisterMenuCommand | |
| // @grant GM_getValue | |
| // @grant GM_setValue | |
| // @run-at document-start | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| // ── Configuration ────────────────────────────────────── | |
| // Position: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | |
| const TOGGLE_POSITION = 'bottom-right'; | |
| const TOGGLE_MARGIN = '12px'; | |
| // ── State ────────────────────────────────────────────── | |
| const STORAGE_KEY = 'darkmode_enabled_' + location.hostname; | |
| let enabled = GM_getValue(STORAGE_KEY, false); | |
| let showPill = GM_getValue('darkmode_show_pill', true); | |
| let styleEl = null; | |
| let toggleEl = null; | |
| let pillStyleEl = null; | |
| let menuCommandId = null; | |
| let pillMenuId = null; | |
| // ── Dark mode CSS ────────────────────────────────────── | |
| const DARK_CSS = ` | |
| html { | |
| filter: invert(100%) hue-rotate(180deg) brightness(105%) contrast(90%) !important; | |
| } | |
| img, | |
| video, | |
| canvas, | |
| [style*="background-image"] { | |
| filter: invert(100%) hue-rotate(180deg) !important; | |
| } | |
| `; | |
| // ── Pill toggle CSS ──────────────────────────────────── | |
| const TOGGLE_CSS = ` | |
| #darkmode-pill { | |
| position: fixed; | |
| z-index: 2147483647; | |
| width: 40px; | |
| height: 22px; | |
| border-radius: 11px; | |
| cursor: pointer; | |
| transition: background-color 0.2s ease, opacity 0.2s ease; | |
| opacity: 0.5; | |
| border: none; | |
| outline: none; | |
| padding: 0; | |
| display: flex; | |
| align-items: center; | |
| } | |
| #darkmode-pill:hover { | |
| opacity: 1; | |
| } | |
| #darkmode-pill .knob { | |
| width: 16px; | |
| height: 16px; | |
| border-radius: 50%; | |
| background: white; | |
| transition: transform 0.2s ease; | |
| margin: 0 3px; | |
| } | |
| #darkmode-pill[data-enabled="false"] { | |
| background-color: #888; | |
| } | |
| #darkmode-pill[data-enabled="false"] .knob { | |
| transform: translateX(0); | |
| } | |
| #darkmode-pill[data-enabled="true"] { | |
| background-color: #4a9eff; | |
| } | |
| #darkmode-pill[data-enabled="true"] .knob { | |
| transform: translateX(18px); | |
| } | |
| `; | |
| // ── Helpers ───────────────────────────────────────────── | |
| function positionStyles() { | |
| const [v, h] = TOGGLE_POSITION.split('-'); | |
| return `${v}: ${TOGGLE_MARGIN}; ${h}: ${TOGGLE_MARGIN};`; | |
| } | |
| function applyDark() { | |
| if (enabled && !styleEl) { | |
| styleEl = document.createElement('style'); | |
| styleEl.id = 'simple-dark-mode'; | |
| styleEl.textContent = DARK_CSS; | |
| (document.head || document.documentElement).appendChild(styleEl); | |
| } else if (!enabled && styleEl) { | |
| styleEl.remove(); | |
| styleEl = null; | |
| } | |
| } | |
| function updateToggle() { | |
| if (!toggleEl) return; | |
| toggleEl.setAttribute('data-enabled', enabled); | |
| toggleEl.title = enabled ? 'Dark mode ON' : 'Dark mode OFF'; | |
| toggleEl.style.filter = enabled | |
| ? 'invert(100%) hue-rotate(180deg)' | |
| : 'none'; | |
| } | |
| // ── Pill UI ───────────────────────────────────────────── | |
| function createToggle() { | |
| if (!showPill) return; | |
| pillStyleEl = document.createElement('style'); | |
| pillStyleEl.id = 'darkmode-pill-styles'; | |
| pillStyleEl.textContent = TOGGLE_CSS; | |
| (document.head || document.documentElement).appendChild(pillStyleEl); | |
| toggleEl = document.createElement('button'); | |
| toggleEl.id = 'darkmode-pill'; | |
| toggleEl.setAttribute('data-enabled', enabled); | |
| toggleEl.setAttribute('style', positionStyles()); | |
| toggleEl.title = enabled ? 'Dark mode ON' : 'Dark mode OFF'; | |
| const knob = document.createElement('span'); | |
| knob.className = 'knob'; | |
| toggleEl.appendChild(knob); | |
| toggleEl.addEventListener('click', (e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| toggle(); | |
| }); | |
| if (enabled) { | |
| toggleEl.style.filter = 'invert(100%) hue-rotate(180deg)'; | |
| } | |
| document.body.appendChild(toggleEl); | |
| } | |
| function destroyToggle() { | |
| if (toggleEl) { toggleEl.remove(); toggleEl = null; } | |
| if (pillStyleEl) { pillStyleEl.remove(); pillStyleEl = null; } | |
| } | |
| function togglePillVisibility() { | |
| showPill = !showPill; | |
| GM_setValue('darkmode_show_pill', showPill); | |
| if (showPill) { | |
| createToggle(); | |
| updateToggle(); | |
| } else { | |
| destroyToggle(); | |
| } | |
| registerMenu(); | |
| } | |
| // ── Tampermonkey menu ─────────────────────────────────── | |
| function registerMenu() { | |
| if (menuCommandId != null) GM_unregisterMenuCommand(menuCommandId); | |
| if (pillMenuId != null) GM_unregisterMenuCommand(pillMenuId); | |
| menuCommandId = GM_registerMenuCommand( | |
| enabled ? 'Disable Dark Mode' : 'Enable Dark Mode', | |
| toggle | |
| ); | |
| pillMenuId = GM_registerMenuCommand( | |
| showPill ? 'Hide Toggle Pill' : 'Show Toggle Pill', | |
| togglePillVisibility | |
| ); | |
| } | |
| // ── Toggle ────────────────────────────────────────────── | |
| function toggle() { | |
| enabled = !enabled; | |
| GM_setValue(STORAGE_KEY, enabled); | |
| applyDark(); | |
| updateToggle(); | |
| registerMenu(); | |
| } | |
| // ── Keyboard shortcut: Alt+Shift+D ────────────────────── | |
| // KeyD = QWERTY position, KeyS = Colemak position | |
| document.addEventListener('keydown', function (e) { | |
| if (e.altKey && e.shiftKey && (e.code === 'KeyD' || e.code === 'KeyS')) { | |
| e.preventDefault(); | |
| e.stopImmediatePropagation(); | |
| toggle(); | |
| } | |
| }, true); | |
| // ── Init ──────────────────────────────────────────────── | |
| registerMenu(); | |
| if (enabled) { | |
| applyDark(); | |
| } | |
| if (document.body) { | |
| createToggle(); | |
| } else { | |
| const observer = new MutationObserver(() => { | |
| if (document.body) { | |
| observer.disconnect(); | |
| createToggle(); | |
| } | |
| }); | |
| observer.observe(document.documentElement, { childList: true }); | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment