Dark Mode

Add a Dark Mode / Night Mode to your website in a few seconds

目前为 2022-12-21 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Dark Mode
  3. // @version 1
  4. // @description Add a Dark Mode / Night Mode to your website in a few seconds
  5. // @author theabbie
  6. // @match *://www.1point3acres.com/*
  7. // @namespace https://theabbie.github.io
  8. // @license MIT
  9. // ==/UserScript==
  10.  
  11. const IS_BROWSER = true;
  12. class Darkmode {
  13. constructor(options) {
  14. if (!IS_BROWSER) {
  15. return;
  16. }
  17.  
  18. const defaultOptions = {
  19. bottom: '32px',
  20. right: '32px',
  21. left: 'unset',
  22. time: '0.3s',
  23. mixColor: '#fff',
  24. backgroundColor: '#fff',
  25. buttonColorDark: '#100f2c',
  26. buttonColorLight: '#fff',
  27. label: '',
  28. saveInCookies: true,
  29. autoMatchOsTheme: true
  30. };
  31.  
  32. options = Object.assign({}, defaultOptions, options);
  33.  
  34. const css = `
  35. .darkmode-layer {
  36. position: fixed;
  37. pointer-events: none;
  38. background: ${options.mixColor};
  39. transition: all ${options.time} ease;
  40. mix-blend-mode: difference;
  41. }
  42.  
  43. .darkmode-layer--button {
  44. width: 2.9rem;
  45. height: 2.9rem;
  46. border-radius: 50%;
  47. right: ${options.right};
  48. bottom: ${options.bottom};
  49. left: ${options.left};
  50. }
  51.  
  52. .darkmode-layer--simple {
  53. width: 100%;
  54. height: 100%;
  55. top: 0;
  56. left: 0;
  57. transform: scale(1) !important;
  58. }
  59.  
  60. .darkmode-layer--expanded {
  61. transform: scale(100);
  62. border-radius: 0;
  63. }
  64.  
  65. .darkmode-layer--no-transition {
  66. transition: none;
  67. }
  68.  
  69. .darkmode-toggle {
  70. background: ${options.buttonColorDark};
  71. width: 3rem;
  72. height: 3rem;
  73. position: fixed;
  74. border-radius: 50%;
  75. border:none;
  76. right: ${options.right};
  77. bottom: ${options.bottom};
  78. left: ${options.left};
  79. cursor: pointer;
  80. transition: all 0.5s ease;
  81. display: flex;
  82. justify-content: center;
  83. align-items: center;
  84. }
  85.  
  86. .darkmode-toggle--white {
  87. background: ${options.buttonColorLight};
  88. }
  89.  
  90. .darkmode-toggle--inactive {
  91. display: none;
  92. }
  93.  
  94. .darkmode-background {
  95. background: ${options.backgroundColor};
  96. position: fixed;
  97. pointer-events: none;
  98. z-index: -10;
  99. width: 100%;
  100. height: 100%;
  101. top: 0;
  102. left: 0;
  103. }
  104.  
  105. img, .darkmode-ignore {
  106. isolation: isolate;
  107. display: inline-block;
  108. }
  109.  
  110. @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
  111. .darkmode-toggle {display: none !important}
  112. }
  113.  
  114. @supports (-ms-ime-align:auto), (-ms-accelerator:true) {
  115. .darkmode-toggle {display: none !important}
  116. }
  117. `;
  118.  
  119. const layer = document.createElement('div');
  120. const button = document.createElement('button');
  121. const background = document.createElement('div');
  122.  
  123. button.innerHTML = options.label;
  124. button.classList.add('darkmode-toggle--inactive');
  125. layer.classList.add('darkmode-layer');
  126. background.classList.add('darkmode-background');
  127.  
  128. const darkmodeActivated = window.localStorage.getItem('darkmode') === 'true';
  129. const preferedThemeOs =
  130. options.autoMatchOsTheme && window.matchMedia('(prefers-color-scheme: dark)').matches;
  131. const darkmodeNeverActivatedByAction = window.localStorage.getItem('darkmode') === null;
  132.  
  133. if (
  134. (darkmodeActivated === true && options.saveInCookies) ||
  135. (darkmodeNeverActivatedByAction && preferedThemeOs)
  136. ) {
  137. layer.classList.add(
  138. 'darkmode-layer--expanded',
  139. 'darkmode-layer--simple',
  140. 'darkmode-layer--no-transition'
  141. );
  142. button.classList.add('darkmode-toggle--white');
  143. document.body.classList.add('darkmode--activated');
  144. }
  145.  
  146. document.body.insertBefore(button, document.body.firstChild);
  147. document.body.insertBefore(layer, document.body.firstChild);
  148. document.body.insertBefore(background, document.body.firstChild);
  149.  
  150. this.addStyle(css);
  151.  
  152. this.button = button;
  153. this.layer = layer;
  154. this.saveInCookies = options.saveInCookies;
  155. this.time = options.time;
  156. }
  157.  
  158. addStyle(css) {
  159. const linkElement = document.createElement('link');
  160.  
  161. linkElement.setAttribute('rel', 'stylesheet');
  162. linkElement.setAttribute('type', 'text/css');
  163. linkElement.setAttribute('href', 'data:text/css;charset=UTF-8,' + encodeURIComponent(css));
  164. document.head.appendChild(linkElement);
  165. }
  166.  
  167. showWidget() {
  168. if (!IS_BROWSER) {
  169. return;
  170. }
  171. const button = this.button;
  172. const layer = this.layer;
  173. const time = parseFloat(this.time) * 1000;
  174.  
  175. button.classList.add('darkmode-toggle');
  176. button.classList.remove('darkmode-toggle--inactive');
  177. button.setAttribute('aria-label', 'Activate dark mode');
  178. button.setAttribute('aria-checked', 'false');
  179. button.setAttribute('role', 'checkbox');
  180. layer.classList.add('darkmode-layer--button');
  181.  
  182. button.addEventListener('click', () => {
  183. const isDarkmode = this.isActivated();
  184.  
  185. if (!isDarkmode) {
  186. layer.classList.add('darkmode-layer--expanded');
  187. button.setAttribute('disabled', true);
  188. setTimeout(() => {
  189. layer.classList.add('darkmode-layer--no-transition');
  190. layer.classList.add('darkmode-layer--simple');
  191. button.removeAttribute('disabled');
  192. }, time);
  193. } else {
  194. layer.classList.remove('darkmode-layer--simple');
  195. button.setAttribute('disabled', true);
  196. setTimeout(() => {
  197. layer.classList.remove('darkmode-layer--no-transition');
  198. layer.classList.remove('darkmode-layer--expanded');
  199. button.removeAttribute('disabled');
  200. }, 1);
  201. }
  202.  
  203. button.classList.toggle('darkmode-toggle--white');
  204. document.body.classList.toggle('darkmode--activated');
  205. window.localStorage.setItem('darkmode', !isDarkmode);
  206. });
  207. }
  208.  
  209. toggle() {
  210. if (!IS_BROWSER) {
  211. return;
  212. }
  213. const layer = this.layer;
  214. const isDarkmode = this.isActivated();
  215. const button = this.button;
  216.  
  217. layer.classList.toggle('darkmode-layer--simple');
  218. document.body.classList.toggle('darkmode--activated');
  219. window.localStorage.setItem('darkmode', !isDarkmode);
  220. button.setAttribute('aria-label', 'De-activate dark mode');
  221. button.setAttribute('aria-checked', 'true');
  222. }
  223.  
  224. isActivated() {
  225. if (!IS_BROWSER) {
  226. return null;
  227. }
  228. return document.body.classList.contains('darkmode--activated');
  229. }
  230. }
  231. new Darkmode().showWidget();