From acb39786a2a1177430b9edb8dfeb58bc454db2ba Mon Sep 17 00:00:00 2001 From: Ricardo Wurmus Date: Tue, 20 Dec 2022 23:44:44 +0100 Subject: Add dark/light theme switcher. --- assets/js/theme-switcher.js | 101 ++++++++++++++++++++++++++++++++++++++++++++ assets/mumi.scss | 1 + mumi/web/view/html.scm | 2 + 3 files changed, 104 insertions(+) create mode 100644 assets/js/theme-switcher.js diff --git a/assets/js/theme-switcher.js b/assets/js/theme-switcher.js new file mode 100644 index 0000000..0cca832 --- /dev/null +++ b/assets/js/theme-switcher.js @@ -0,0 +1,101 @@ +/* + * Theme switcher + * + * Pico.css - https://picocss.com + * Copyright 2019-2022 - Licensed under MIT + */ + +const themeSwitcher = { + // Config + _scheme: 'auto', + change: { + light: 'Turn on dark mode', + dark: 'Turn off dark mode', + }, + buttonsTarget: '.theme-switcher', + localStorageKey: 'picoPreferedColorScheme', + + // Init + init() { + this.scheme = this.schemeFromLocalStorage; + this.initSwitchers(); + }, + + // Get color scheme from local storage + get schemeFromLocalStorage() { + if (typeof window.localStorage !== 'undefined') { + if (window.localStorage.getItem(this.localStorageKey) !== null) { + return window.localStorage.getItem(this.localStorageKey); + } + } + return this._scheme; + }, + + // Prefered color scheme + get preferedColorScheme() { + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + }, + + // Init switchers + initSwitchers() { + const buttons = document.querySelectorAll(this.buttonsTarget); + buttons.forEach(button => { + button.addEventListener('click', () => { + this.scheme == 'dark' ? this.scheme = 'light' : this.scheme = 'dark'; + }, false); + }); + }, + + // Add new button + addButton(config) { + let button = document.createElement(config.tag); + button.className = config.class; + document.querySelector(config.target).appendChild(button); + }, + + // Set scheme + set scheme(scheme) { + if (scheme == 'auto') { + this.preferedColorScheme == 'dark' ? this._scheme = 'dark' : this._scheme = 'light'; + } + else if (scheme == 'dark' || scheme == 'light') { + this._scheme = scheme; + } + this.applyScheme(); + this.schemeToLocalStorage(); + }, + + // Get scheme + get scheme() { + return this._scheme; + }, + + // Apply scheme + applyScheme() { + document.querySelector('html').setAttribute('data-theme', this.scheme); + const buttons = document.querySelectorAll(this.buttonsTarget); + buttons.forEach( + button => { + const text = this.scheme == 'dark' ? this.change.dark : this.change.light; + button.innerHTML = text; + button.setAttribute('aria-label', text.replace(/<[^>]*>?/gm, '')); + } + ); + }, + + // Store scheme to local storage + schemeToLocalStorage() { + if (typeof window.localStorage !== 'undefined') { + window.localStorage.setItem(this.localStorageKey, this.scheme); + } + }, +}; + +window.addEventListener('load', function () { + themeSwitcher.addButton({ + tag: 'BUTTON', + class: 'contrast switcher theme-switcher', + target: 'body', + }); + themeSwitcher.init(); +}); diff --git a/assets/mumi.scss b/assets/mumi.scss index ae654bc..474fd88 100644 --- a/assets/mumi.scss +++ b/assets/mumi.scss @@ -7,6 +7,7 @@ $commit_header: #005cc5; // Import full Pico source code @import "pico/scss/pico"; +@import "pico/docs/scss/components/_theme-switcher.scss"; :root { --spacing: .5em; diff --git a/mumi/web/view/html.scm b/mumi/web/view/html.scm index bdc217b..dedb920 100644 --- a/mumi/web/view/html.scm +++ b/mumi/web/view/html.scm @@ -63,6 +63,8 @@ (media "screen") (type "text/css") (href "/css/mumi.css?20221220000000"))) + (script + (@ (src "/js/theme-switcher.js"))) ,@head) (body ,@body (footer (@ (class "text-center")) -- cgit v1.2.3