/** * --- Alpine helper functions */ /** * Set an alpine.js property * * @function setProp * @param {string} key - Key of the data property * @param {*} prop - The data property * @param {string} selector - The jQuery selector to the root element */ const setProp = (key, prop, selector = '#root') => { $(selector).get(0).__x.$data[key] = prop; }; /** * Get an alpine.js property * * @function getProp * @param {string} key - Key of the data property * @param {string} selector - The jQuery selector to the root element * @return {*} The data property */ const getProp = (key, selector = '#root') => { return $(selector).get(0).__x.$data[key]; }; /** * --- Theme related functions * Note: In the comments below we treat "theme" and "theme setting" * differently. A theme can have only two values, either "dark" or * "light", while a theme setting can have the third value "system". */ /** * Check if the system setting prefers dark theme. * from https://flaviocopes.com/javascript-detect-dark-mode/ * * @function preferDarkMode * @return {bool} */ const preferDarkMode = () => { return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; }; /** * Check whether a given string represents a valid theme setting * * @function validThemeSetting * @param {string} theme - The string representing the theme setting * @return {bool} */ const validThemeSetting = (theme) => { return ['dark', 'light', 'system'].indexOf(theme) >= 0; }; /** * Load theme setting from local storage, or use 'light' * * @function loadThemeSetting * @return {string} A theme setting ('dark', 'light', or 'system') */ const loadThemeSetting = () => { let str = localStorage.getItem('theme'); if (!str || !validThemeSetting(str)) str = 'system'; return str; }; /** * Load the current theme (not theme setting) * * @function loadTheme * @return {string} The current theme to use ('dark' or 'light') */ const loadTheme = () => { let setting = loadThemeSetting(); if (setting === 'system') { setting = preferDarkMode() ? 'dark' : 'light'; } return setting; }; /** * Save a theme setting * * @function saveThemeSetting * @param {string} setting - A theme setting */ const saveThemeSetting = setting => { if (!validThemeSetting(setting)) setting = 'system'; localStorage.setItem('theme', setting); }; /** * Toggle the current theme. When the current theme setting is 'system', it * will be changed to either 'light' or 'dark' * * @function toggleTheme */ const toggleTheme = () => { const theme = loadTheme(); const newTheme = theme === 'dark' ? 'light' : 'dark'; saveThemeSetting(newTheme); setTheme(newTheme); }; /** * Apply a theme, or load a theme and then apply it * * @function setTheme * @param {string?} theme - (Optional) The theme to apply. When omitted, use * `loadTheme` to get a theme and apply it. */ const setTheme = (theme) => { if (!theme) theme = loadTheme(); if (theme === 'dark') { $('html').css('background', 'rgb(20, 20, 20)'); $('body').addClass('uk-light'); $('.ui-widget-content').addClass('dark'); } else { $('html').css('background', ''); $('body').removeClass('uk-light'); $('.ui-widget-content').removeClass('dark'); } }; // do it before document is ready to prevent the initial flash of white on // most pages setTheme(); $(() => { // hack for the reader page setTheme(); // on system dark mode setting change if (window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)') .addEventListener('change', event => { if (loadThemeSetting() === 'system') setTheme(event.matches ? 'dark' : 'light'); }); } });