Dark Mode Auto Toggle

Enable dark mode with a toggle button and automatically based on location

  1. // ==UserScript==
  2. // @name Dark Mode Auto Toggle
  3. // @version 0.2.0
  4. // @description Enable dark mode with a toggle button and automatically based on location
  5. // @namespace https://greasyfork.org/en/users/28298
  6. // @author https://greasyfork.org/en/users/28298
  7. // @homepage https://greasyfork.org/en/scripts/523690
  8. // @license MIT
  9. // @match *://*/*
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // ==/UserScript==
  13.  
  14. // @grant none
  15. // original author: Michael Wang https://greasyfork.org/en/scripts/472251-dark-mode/code
  16. // with help of claude ai
  17.  
  18. (function() {
  19. 'use strict';
  20.  
  21. //////////////////////////////////////////////////////////////////////////
  22. // manually tell domain's original mode, because auto detection would fail
  23. const domainModes = {
  24. 'claude.ai' : 'bright',
  25. 'chat.openai.com' : 'dark',
  26. 'deepseek.com' : 'dark',
  27. 'linux.do' : 'dark',
  28. 'duckduckgo.com' : 'dark',
  29. 'www.google.com' : 'dark',
  30. 'youtube.com' : 'dark',
  31. 'greasyfork.org' : 'bright',
  32. 'web.telegram.org': 'dark',
  33. '192.168.1.1' : 'dark',
  34. '192.168.1.2' : 'dark',
  35. '192.168.1.3' : 'dark',
  36. '127.0.0.1' : 'dark',
  37. };
  38. /////////////////////////////////////////////////////////////////////////
  39.  
  40. // Create style element for dark mode
  41. const darkStyle = document.createElement('style');
  42. darkStyle.textContent = `
  43. html {
  44. filter: invert(1) hue-rotate(180deg) contrast(0.8);
  45. }
  46. /** reverse filter for media elements */
  47. img, video, picture, canvas, iframe, embed {
  48. filter: invert(1) hue-rotate(180deg);
  49. }
  50. `;
  51.  
  52. // Initialize based on page's current state
  53. let darkMode = false; // Start with no filter
  54.  
  55. // Function to detect if page is already dark
  56. function isPageDark() {
  57. const url = window.location.hostname;
  58. // Check if domain is in our list
  59. for (const domain in domainModes) {
  60. if (url.includes(domain)) {
  61. return domainModes[domain] === 'dark';
  62. }
  63. }
  64.  
  65. // auto detection
  66. function isDarkMode() {
  67. try {
  68. const bgcolor = window.getComputedStyle(document.body).backgroundColor;
  69. const rgb = color.match(/\d+/g);
  70. if (!rgb) return 255 < 128; // Default to light if can't detect
  71. // Use perceived brightness formula
  72. return (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000 < 128;
  73. } catch (e) {
  74. return false;
  75. }
  76. }
  77. function checkDarkModeClasses() {
  78. const html = document.documentElement;
  79. const body = document.body;
  80.  
  81. const darkClasses = ['dark', 'dark-mode', 'dark-theme', 'theme-dark'];
  82. return darkClasses.some(className =>
  83. html.classList.contains(className) || body.classList.contains(className)
  84. );
  85. }
  86. function checkDarkModeAttributes() {
  87. const html = document.documentElement;
  88.  
  89. return html.getAttribute('data-theme') === 'dark' ||
  90. html.getAttribute('data-color-mode') === 'dark';
  91. }
  92.  
  93. // Check computed colors
  94. const isDarkByColor = isDarkMode();
  95. // Check classes and attributes
  96. const isDarkByClass = checkDarkModeClasses();
  97. const isDarkByAttribute = checkDarkModeAttributes();
  98. // Site is likely in dark mode if any of these are true
  99.  
  100. return isDarkByClass || isDarkByAttribute || isDarkByColor;
  101. }
  102.  
  103. // Initialize darkMode based on stored preference or page state
  104. const pageIsDark = isPageDark();
  105.  
  106. // Create toggle button
  107. const button = document.createElement('button');
  108. button.innerHTML = pageIsDark ? '☀️' : '🌛';
  109. button.style.position = 'fixed';
  110. button.style.top = '45px';
  111. button.style.right = '10px';
  112. button.style.zIndex = '1000';
  113. button.style.background = 'none';
  114. button.style.border = 'none';
  115. button.style.fontSize = '24px';
  116. button.style.cursor = 'pointer';
  117. button.style.color = 'inherit';
  118.  
  119. // Function to get sunrise/sunset times
  120. async function getSunTimes() {
  121. // approximate times
  122. const now = new Date();
  123. return {
  124. sunrise: new Date(now.setHours(8, 0, 0, 0)),
  125. sunset: new Date(now.setHours(18, 0, 0, 0))
  126. };
  127. }
  128.  
  129. // Function to toggle dark mode
  130. function toggleDarkMode(forceDark) {
  131. if (forceDark !== undefined) {
  132. // Auto dark mode (sunrise/sunset)
  133. if (pageIsDark) {
  134. // If page is already dark, don't change anything
  135. return;
  136. }
  137. darkMode = forceDark;
  138. } else {
  139. // Manual toggle - always allow regardless of pageIsDark
  140. darkMode = !darkMode;
  141. }
  142.  
  143. if (darkMode) {
  144. document.head.appendChild(darkStyle);
  145. button.innerHTML = pageIsDark ? '🌛' : '☀️';
  146. } else {
  147. if (darkStyle.parentNode) {
  148. document.head.removeChild(darkStyle);
  149. }
  150. button.innerHTML = pageIsDark ? '☀️' : '🌛';
  151. }
  152. }
  153.  
  154. // Function to check and update dark mode based on time
  155. async function updateDarkMode() {
  156. const sunTimes = await getSunTimes();
  157. const now = new Date();
  158. const isDark = now < sunTimes.sunrise || now > sunTimes.sunset;
  159. toggleDarkMode(isDark);
  160. }
  161.  
  162. // Add settings button
  163. const settingsButton = document.createElement('button');
  164. settingsButton.innerHTML = '⚙';
  165. settingsButton.style.position = 'fixed';
  166. settingsButton.style.top = '40px';
  167. settingsButton.style.right = '15px';
  168. settingsButton.style.zIndex = '1000';
  169. settingsButton.style.background = 'none';
  170. settingsButton.style.border = 'none';
  171. settingsButton.style.fontSize = '12px';
  172. settingsButton.style.cursor = 'pointer';
  173. settingsButton.style.color = 'inherit';
  174.  
  175. // Settings panel to reset location
  176. settingsButton.addEventListener('click', () => {
  177. if (confirm('Reset location settings?')) {
  178. GM_setValue('userLocation', null);
  179. location.reload();
  180. }
  181. });
  182.  
  183. // Initial update
  184. updateDarkMode();
  185.  
  186. // Update every half hour (1800000)
  187. setInterval(updateDarkMode, 1800000);
  188.  
  189. // Manual toggle still works
  190. button.addEventListener('click', () => toggleDarkMode());
  191.  
  192. // Add buttons to page
  193. document.body.appendChild(button);
  194. })();