Files
hextra_mirror/assets/js/theme.js
Ludovic Fernandez 18a9335d4b feat: add 'system' inside the theme toggle (#766)
* feat: add 'system' inside the theme toggle

* chore: generate hugo_stats.json

* fix: missing css

* chore: reorganize code

* feat: menu

* chore: simplify

* chore: some i18n

* review

* fix: remove replace
2025-08-19 23:26:32 +01:00

119 lines
3.7 KiB
JavaScript

// Light / Dark theme toggle
(function () {
const defaultTheme = '{{ site.Params.theme.default | default `system`}}'
const themeToggleButtons = document.querySelectorAll(".hextra-theme-toggle");
const themeToggleOptions = document.querySelectorAll(".hextra-theme-toggle-options p");
function setSystemTheme() {
const prefersColorScheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
document.documentElement.classList.remove("dark", "light");
document.documentElement.classList.add(prefersColorScheme);
document.documentElement.style.colorScheme = prefersColorScheme;
}
function applyTheme(theme) {
themeToggleButtons.forEach((btn) => btn.parentElement.dataset.theme = theme );
localStorage.setItem("color-theme", theme);
}
function switchTheme(theme) {
switch (theme) {
case "light":
document.documentElement.classList.remove("dark");
document.documentElement.classList.add(theme);
document.documentElement.style.colorScheme = theme;
applyTheme(theme);
break;
case "dark":
document.documentElement.classList.remove("light");
document.documentElement.classList.add(theme);
document.documentElement.style.colorScheme = theme;
applyTheme(theme);
break;
default:
setSystemTheme();
applyTheme("system");
break;
}
}
const colorTheme = "color-theme" in localStorage ? localStorage.getItem("color-theme") : defaultTheme;
switchTheme(colorTheme);
// Add click event handler to the menu items.
themeToggleOptions.forEach((option) => {
option.addEventListener("click", function (e) {
e.preventDefault();
switchTheme(option.dataset.item);
})
})
// Add click event handler to the buttons
themeToggleButtons.forEach((toggler) => {
toggler.addEventListener("click", function (e) {
e.preventDefault();
const optionsElement = toggler.nextElementSibling;
optionsElement.classList.toggle('hx:hidden');
// Calculate the position of a language options element.
const switcherRect = toggler.getBoundingClientRect();
// Must be called before optionsElement.clientWidth.
optionsElement.style.minWidth = `${Math.max(switcherRect.width, 50)}px`;
const isOnTop = toggler.dataset.location === 'top';
const isOnBottom = toggler.dataset.location === 'bottom';
const isOnBottomRight = toggler.dataset.location === 'bottom-right';
const isRTL = document.body.dir === 'rtl'
// Stuck on the left side of the switcher.
let translateX = switcherRect.left;
if (isOnTop && !isRTL || isOnBottom && isRTL || isOnBottomRight && !isRTL) {
// Stuck on the right side of the switcher.
translateX = switcherRect.right - optionsElement.clientWidth;
}
// Stuck on the top of the switcher.
let translateY = switcherRect.top - window.innerHeight - 15;
if (isOnTop) {
// Stuck on the bottom of the switcher.
translateY = switcherRect.top - window.innerHeight + 150;
}
optionsElement.style.transform = `translate3d(${translateX}px, ${translateY}px, 0)`;
});
});
// Dismiss the menu when clicking outside
document.addEventListener('click', (e) => {
if (e.target.closest('.hextra-theme-toggle') === null) {
themeToggleButtons.forEach((toggler) => {
toggler.dataset.state = 'closed';
toggler.nextElementSibling.classList.add('hx:hidden');
});
}
});
// Listen for system theme changes
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
if (localStorage.getItem("color-theme") === "system") {
setSystemTheme();
}
});
})();