type colorScheme = 'light' | 'dark' | 'auto'; class StackColorScheme { private localStorageKey = 'StackColorScheme'; private currentScheme: colorScheme; private systemPreferScheme: colorScheme; constructor(toggleEl: HTMLElement) { this.bindMatchMedia(); this.currentScheme = this.getSavedScheme(); this.dispatchEvent(document.body.dataset.scheme as colorScheme); if (toggleEl) this.bindClick(toggleEl); if (document.body.style.transition == '') document.body.style.setProperty('transition', 'background-color .3s ease'); } private saveScheme() { localStorage.setItem(this.localStorageKey, this.currentScheme); } private bindClick(toggleEl) { toggleEl.addEventListener('click', (e) => { if (this.isDark()) { /// Disable dark mode this.currentScheme = 'light'; } else { this.currentScheme = 'dark'; } this.setBodyClass(); if (this.currentScheme == this.systemPreferScheme) { /// Set to auto this.currentScheme = 'auto'; } this.saveScheme(); }) } private isDark() { return (this.currentScheme == 'dark' || this.currentScheme == 'auto' && this.systemPreferScheme == 'dark'); } private dispatchEvent(colorScheme: colorScheme) { const event = new CustomEvent('onColorSchemeChange', { detail: colorScheme }); window.dispatchEvent(event); } private setBodyClass() { if (this.isDark()) { document.body.dataset.scheme = 'dark'; } else { document.body.dataset.scheme = 'light'; } this.dispatchEvent(document.body.dataset.scheme as colorScheme); } private getSavedScheme(): colorScheme { const savedScheme = localStorage.getItem(this.localStorageKey); if (savedScheme == 'light' || savedScheme == 'dark' || savedScheme == 'auto') return savedScheme; else return 'auto'; } private bindMatchMedia() { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { if (e.matches) { this.systemPreferScheme = 'dark'; } else { this.systemPreferScheme = 'light'; } this.setBodyClass(); }); } } export default StackColorScheme;