diff --git a/netbox/project-static/js/setmode.js b/netbox/project-static/js/setmode.js index 9418a9f39..45e9eade7 100644 --- a/netbox/project-static/js/setmode.js +++ b/netbox/project-static/js/setmode.js @@ -1,10 +1,15 @@ /** * Set the color mode on the `
` element and in local storage. * - * @param mode {"dark" | "light"} UI color mode. + * @param mode {"dark" | "light" | "auto"} UI color mode. */ function setMode(mode) { - document.documentElement.setAttribute("data-bs-theme", mode); + document.documentElement.setAttribute( + "data-bs-theme", + mode === "auto" + ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + : mode + ); localStorage.setItem("netbox-color-mode", mode); } @@ -23,6 +28,12 @@ function initMode() { if (clientMode !== null) { return setMode(clientMode, false); } + // If client wants to auto-switch, attach an onchange listener to the media query + if (clientMode === 'auto') { + window.matchMedia('(prefers-color-scheme: light)').onchange = event => { + document.documentElement.dataset.bsTheme = (e.matches) ? 'light' : 'dark'; + } + } // Fall back to the mode preferred by the browser, if specified if (preferDark) { diff --git a/netbox/project-static/src/colorMode.ts b/netbox/project-static/src/colorMode.ts index 453617740..a086d2e32 100644 --- a/netbox/project-static/src/colorMode.ts +++ b/netbox/project-static/src/colorMode.ts @@ -1,6 +1,6 @@ import { getElements, isTruthy } from './util'; -const COLOR_MODE_KEY = 'netbox-color-mode'; +const COLOR_MODE_PREFERENCE_KEY = 'netbox-color-mode-preference'; /** * Determine if a value is a supported color mode string value. @@ -9,28 +9,41 @@ function isColorMode(value: unknown): value is ColorMode { return value === 'dark' || value === 'light'; } +function isDefinedColorModePreference(value: unknown): value is ColorModePreference { + return value === 'auto' || isColorMode(value); +} + + /** * Set the color mode to light or dark. * - * @param mode `'light'` or `'dark'` + * @param mode `'light'`, `'dark'` or `'auto'` * @returns `true` if the color mode was successfully set, `false` if not. */ -function storeColorMode(mode: ColorMode): void { - return localStorage.setItem(COLOR_MODE_KEY, mode); +function storeColorMode(modePreference: ColorModePreference): void { + return localStorage.setItem(COLOR_MODE_PREFERENCE_KEY, mode); } -function updateElements(targetMode: ColorMode): void { +function updateElements(targetMode: ColorModePreference): void { const body = document.querySelector('body'); - if (body && targetMode == 'dark') { - body.setAttribute('data-bs-theme', 'dark'); - } else if (body) { - body.setAttribute('data-bs-theme', 'light'); + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const theme = (targetMode === 'auto') + ? (mediaQuery.matches ? 'dark' : 'light' + : (targetMode === 'none' ? targetMode : 'light'); + + if (body) { + body.setAttribute('data-bs-theme', theme); + } + if (body && targetMode === 'auto') { + mediaQuery.onchange = event => { + body.setAttribute('data-bs-theme', event.matches ? 'dark' : 'light'); + } } for (const elevation of getElements