diff --git a/wowchemy/assets/js/wowchemy-theming.js b/wowchemy/assets/js/wowchemy-theming.js index d7222d4f..dc0fe270 100644 --- a/wowchemy/assets/js/wowchemy-theming.js +++ b/wowchemy/assets/js/wowchemy-theming.js @@ -8,6 +8,8 @@ import {fadeIn} from './wowchemy-animation'; +const body = document.body; + function getThemeMode() { return parseInt(localStorage.getItem('wcTheme') || 2); } @@ -17,6 +19,8 @@ function canChangeTheme() { return Boolean(window.wc.darkLightEnabled); } +// initThemeVariation is first called directly after to prevent +// flashing between the default theme mode and the user's choice. function initThemeVariation() { if (!canChangeTheme()) { return; @@ -44,11 +48,14 @@ function initThemeVariation() { } break; } - if (isDarkTheme) { + if (isDarkTheme && !body.classList.contains('dark')) { + console.debug('Applying Wowchemy dark theme'); document.body.classList.add("dark"); - } else { + } else if (body.classList.contains('dark')) { + console.debug('Applying Wowchemy light theme'); document.body.classList.remove("dark"); } + return {isDarkTheme, currentThemeMode}; } function changeThemeModeClick(newMode) { @@ -62,7 +69,6 @@ function changeThemeModeClick(newMode) { localStorage.setItem('wcTheme', '1'); isDarkTheme = true; console.info('User changed theme variation to Dark.'); - showActiveTheme(0); break; case 1: localStorage.setItem('wcTheme', '2'); @@ -77,16 +83,14 @@ function changeThemeModeClick(newMode) { isDarkTheme = window.wc.isSiteThemeDark; } console.info('User changed theme variation to Auto.'); - showActiveTheme(1); break; default: localStorage.setItem('wcTheme', '0'); isDarkTheme = false; console.info('User changed theme variation to Light.'); - showActiveTheme(2); break; } - renderThemeVariation(isDarkTheme); + renderThemeVariation(isDarkTheme, newMode); } function showActiveTheme(mode) { @@ -119,16 +123,19 @@ function showActiveTheme(mode) { * Render theme variation (day or night). * * @param {boolean} isDarkTheme - * @param {boolean} init + * @param {int} themeMode - {0: Dark, 1: Auto, 2: Light} + * @param {boolean} init - true only when called on document ready * @returns {undefined} */ -function renderThemeVariation(isDarkTheme, init = false) { +function renderThemeVariation(isDarkTheme, themeMode = 2, init = false) { // Is code highlighting enabled in site config? const codeHlLight = document.querySelector('link[title=hl-light]'); const codeHlDark = document.querySelector('link[title=hl-dark]'); - const codeHlEnabled = codeHlLight || codeHlDark; - const diagramEnabled = document.querySelector('script[title=mermaid]'); - const body = document.body; + const codeHlEnabled = (codeHlLight !== null) || (codeHlDark !== null); + const diagramEnabled = document.querySelector('script[title=mermaid]') !== null; + + // Update active theme mode in navbar theme selector. + showActiveTheme(themeMode); // Check if re-render required. if (!init) { @@ -147,10 +154,16 @@ function renderThemeVariation(isDarkTheme, init = false) { } body.classList.remove('dark'); if (codeHlEnabled) { - codeHlLight.disabled = false; - codeHlDark.disabled = true; + console.debug('Setting HLJS theme to light'); + if (codeHlLight) { + codeHlLight.disabled = false; + } + if (codeHlDark){ + codeHlDark.disabled = true; + } } if (diagramEnabled) { + console.debug('Initializing Mermaid with light theme'); if (init) { /** @namespace window.mermaid **/ window.mermaid.initialize({theme: 'default', securityLevel: 'loose'}); @@ -167,10 +180,16 @@ function renderThemeVariation(isDarkTheme, init = false) { } body.classList.add("dark"); if (codeHlEnabled) { - codeHlLight.disabled = true; - codeHlDark.disabled = false; + console.debug('Setting HLJS theme to dark'); + if (codeHlLight) { + codeHlLight.disabled = true; + } + if (codeHlDark){ + codeHlDark.disabled = false; + } } if (diagramEnabled) { + console.debug('Initializing Mermaid with dark theme'); if (init) { /** @namespace window.mermaid **/ window.mermaid.initialize({theme: 'dark', securityLevel: 'loose'}); @@ -182,6 +201,35 @@ function renderThemeVariation(isDarkTheme, init = false) { } } +/** + * onMediaQueryListEvent. + * + * @param {MediaQueryListEvent} event + * @returns {undefined} + */ +function onMediaQueryListEvent(event) { + if (!canChangeTheme()) { + // Changing theme variation is not allowed by admin. + return; + } + const darkModeOn = event.matches; + console.debug(`OS dark mode preference changed to ${darkModeOn ? '🌒 on' : '☀️ off'}.`); + let currentThemeVariation = getThemeMode(); + let isDarkTheme; + if (currentThemeVariation === 2) { + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + // The visitor prefers dark themes. + isDarkTheme = true; + } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { + // The visitor prefers light themes. + isDarkTheme = false; + } else { + // The visitor does not have a day or night preference, so use the theme's default setting. + isDarkTheme = window.wc.isSiteThemeDark; + } + renderThemeVariation(isDarkTheme, currentThemeVariation); + } +} export { canChangeTheme, @@ -189,4 +237,5 @@ export { changeThemeModeClick, renderThemeVariation, getThemeMode, + onMediaQueryListEvent, }; diff --git a/wowchemy/assets/js/wowchemy.js b/wowchemy/assets/js/wowchemy.js index 241a09f9..90cc8f1a 100644 --- a/wowchemy/assets/js/wowchemy.js +++ b/wowchemy/assets/js/wowchemy.js @@ -8,11 +8,10 @@ import {hugoEnvironment} from '@params'; import { - canChangeTheme, changeThemeModeClick, - getThemeMode, initThemeVariation, - renderThemeVariation + renderThemeVariation, + onMediaQueryListEvent, } from './wowchemy-theming'; console.debug(`Environment: ${hugoEnvironment}`) @@ -426,49 +425,9 @@ $(document).ready(function () { hljs.initHighlighting(); } - // Initialize theme variation. - initThemeVariation(); - - // Change theme mode. - $('.js-set-theme-light').click(function (e) { - e.preventDefault(); - changeThemeModeClick(2); - }); - $('.js-set-theme-dark').click(function (e) { - e.preventDefault(); - changeThemeModeClick(0); - }); - $('.js-set-theme-auto').click(function (e) { - e.preventDefault(); - changeThemeModeClick(1); - }); - - // Live update of day/night mode on system preferences update (no refresh required). - // Note: since we listen only for *dark* events, we won't detect other scheme changes such as light to no-preference. - const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - darkModeMediaQuery.addEventListener("change", (e) => { - if (!canChangeTheme()) { - // Changing theme variation is not allowed by admin. - return; - } - const darkModeOn = e.matches; - console.log(`OS dark mode preference changed to ${darkModeOn ? '🌒 on' : '☀️ off'}.`); - let currentThemeVariation = getThemeMode(); - let isDarkTheme; - if (currentThemeVariation === 2) { - if (window.matchMedia('(prefers-color-scheme: dark)').matches) { - // The visitor prefers dark themes. - isDarkTheme = true; - } else if (window.matchMedia('(prefers-color-scheme: light)').matches) { - // The visitor prefers light themes. - isDarkTheme = false; - } else { - // The visitor does not have a day or night preference, so use the theme's default setting. - isDarkTheme = window.wc.isSiteThemeDark; - } - renderThemeVariation(isDarkTheme); - } - }); + // Render theme variation, including any HLJS and Mermaid themes. + let {isDarkTheme, themeMode} = initThemeVariation(); + renderThemeVariation(isDarkTheme, themeMode, true); }); /* --------------------------------------------------------------------------- @@ -630,8 +589,37 @@ $(window).on('load', function () { fixScrollspy(); }); -// Normalize Bootstrap carousel slide heights. -$(window).on('load resize orientationchange', normalizeCarouselSlideHeights); +// Theme chooser events. +let linkLight = document.querySelector('.js-set-theme-light'); +let linkDark = document.querySelector('.js-set-theme-dark'); +let linkAuto = document.querySelector('.js-set-theme-auto'); +if (linkLight && linkDark && linkAuto) { + linkLight.addEventListener('click', event => { + event.preventDefault(); + changeThemeModeClick(2); + }); + linkDark.addEventListener('click', event => { + event.preventDefault(); + changeThemeModeClick(0); + }); + linkAuto.addEventListener('click', event => { + event.preventDefault(); + changeThemeModeClick(1); + }); +} + +// Media Query events. +// Live update of day/night mode on system preferences update (no refresh required). +// Note: since we listen only for *dark* events, we won't detect other scheme changes such as light to no-preference. +const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); +darkModeMediaQuery.addEventListener("change", (event) => { + onMediaQueryListEvent(event); +}); + +// Normalize Bootstrap carousel slide heights for Slider widget instances. +window.addEventListener('load', normalizeCarouselSlideHeights); +window.addEventListener('resize', normalizeCarouselSlideHeights); +window.addEventListener('orientationchange', normalizeCarouselSlideHeights); // Automatic main menu dropdowns on mouse over. $('body').on('mouseenter mouseleave', '.dropdown', function (e) {