Skip to content

Instantly share code, notes, and snippets.

@mattmc3
Last active March 18, 2026 14:01
Show Gist options
  • Select an option

  • Save mattmc3/96ca5dd86a450702b8267afb67a30b47 to your computer and use it in GitHub Desktop.

Select an option

Save mattmc3/96ca5dd86a450702b8267afb67a30b47 to your computer and use it in GitHub Desktop.
TamperMonkey script to toggle dark mode
// ==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