Add ThemeIcon component to switch between dark and light mode
Introduces a new component 'ThemeIcon' to enable a smooth switch between light and dark themes based on user preference. This improvement includes a drop-down menu for the users to select their preferred mode and also defines theme-related functionalities inside an object called 'HSThemeAppearance'. Further, it incorporates respective event listeners to effectively handle theme change events.
This commit is contained in:
parent
a12fb8be40
commit
d19d0d799d
1 changed files with 172 additions and 0 deletions
172
src/components/ThemeIcon.astro
Normal file
172
src/components/ThemeIcon.astro
Normal file
|
@ -0,0 +1,172 @@
|
|||
<div
|
||||
class="hs-dropdown"
|
||||
data-hs-dropdown-placement="bottom-right"
|
||||
data-hs-dropdown-offset="30"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="hs-dropdown-toggle hs-dark-mode group flex items-center rounded-lg font-medium text-neutral-600 outline-none ring-zinc-500 hover:text-[#fa5a15] dark:text-neutral-400 dark:ring-zinc-200 dark:hover:text-[#fb713b] dark:focus:outline-none"
|
||||
>
|
||||
<svg
|
||||
class="block h-4 w-4 hs-dark-mode-active:hidden"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"></path></svg
|
||||
>
|
||||
<svg
|
||||
class="hidden h-4 w-4 hs-dark-mode-active:block"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
><circle cx="12" cy="12" r="4"></circle><path d="M12 8a2 2 0 1 0 4 4"
|
||||
></path><path d="M12 2v2"></path><path d="M12 20v2"></path><path
|
||||
d="m4.93 4.93 1.41 1.41"></path><path d="m17.66 17.66 1.41 1.41"
|
||||
></path><path d="M2 12h2"></path><path d="M20 12h2"></path><path
|
||||
d="m6.34 17.66-1.41 1.41"></path><path d="m19.07 4.93-1.41 1.41"
|
||||
></path></svg
|
||||
>
|
||||
</button>
|
||||
|
||||
<div
|
||||
id="selectThemeDropdown"
|
||||
class="hs-dropdown-menu z-10 mb-2 mt-2 hidden origin-bottom-left space-y-1 rounded-lg bg-neutral-100 p-2 opacity-0 shadow-md transition-[margin,opacity] duration-300 hs-dropdown-open:opacity-100 dark:divide-neutral-700 dark:border dark:border-neutral-700 dark:bg-neutral-700"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-800 hover:bg-neutral-200 hs-auto-mode-active:bg-gray-100 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-300"
|
||||
data-hs-theme-click-value="auto"
|
||||
>
|
||||
Auto (system default)
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-800 hover:bg-neutral-200 hs-auto-mode-active:bg-gray-100 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-300"
|
||||
data-hs-theme-click-value="default"
|
||||
>
|
||||
Default (light mode)
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-800 hover:bg-neutral-200 hs-auto-mode-active:bg-gray-100 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-300"
|
||||
data-hs-theme-click-value="dark"
|
||||
>
|
||||
Dark
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script is:inline>
|
||||
const HSThemeAppearance = {
|
||||
init() {
|
||||
const defaultTheme = "default";
|
||||
let theme = localStorage.getItem("hs_theme") || defaultTheme;
|
||||
|
||||
if (document.querySelector("html").classList.contains("dark")) return;
|
||||
this.setAppearance(theme);
|
||||
},
|
||||
_resetStylesOnLoad() {
|
||||
const $resetStyles = document.createElement("style");
|
||||
$resetStyles.innerText = `*{transition: unset !important;}`;
|
||||
$resetStyles.setAttribute("data-hs-appearance-onload-styles", "");
|
||||
document.head.appendChild($resetStyles);
|
||||
return $resetStyles;
|
||||
},
|
||||
setAppearance(theme, saveInStore = true, dispatchEvent = true) {
|
||||
const $resetStylesEl = this._resetStylesOnLoad();
|
||||
|
||||
if (saveInStore) {
|
||||
localStorage.setItem("hs_theme", theme);
|
||||
}
|
||||
|
||||
if (theme === "auto") {
|
||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "default";
|
||||
}
|
||||
|
||||
document.querySelector("html").classList.remove("dark");
|
||||
document.querySelector("html").classList.remove("default");
|
||||
document.querySelector("html").classList.remove("auto");
|
||||
|
||||
document
|
||||
.querySelector("html")
|
||||
.classList.add(this.getOriginalAppearance());
|
||||
|
||||
setTimeout(() => {
|
||||
$resetStylesEl.remove();
|
||||
});
|
||||
|
||||
if (dispatchEvent) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("on-hs-appearance-change", { detail: theme }),
|
||||
);
|
||||
}
|
||||
},
|
||||
getAppearance() {
|
||||
let theme = this.getOriginalAppearance();
|
||||
if (theme === "auto") {
|
||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "default";
|
||||
}
|
||||
return theme;
|
||||
},
|
||||
getOriginalAppearance() {
|
||||
const defaultTheme = "default";
|
||||
return localStorage.getItem("hs_theme") || defaultTheme;
|
||||
},
|
||||
};
|
||||
HSThemeAppearance.init();
|
||||
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", (e) => {
|
||||
if (HSThemeAppearance.getOriginalAppearance() === "auto") {
|
||||
HSThemeAppearance.setAppearance("auto", false);
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
const $clickableThemes = document.querySelectorAll(
|
||||
"[data-hs-theme-click-value]",
|
||||
);
|
||||
const $switchableThemes = document.querySelectorAll(
|
||||
"[data-hs-theme-switch]",
|
||||
);
|
||||
|
||||
$clickableThemes.forEach(($item) => {
|
||||
$item.addEventListener("click", () =>
|
||||
HSThemeAppearance.setAppearance(
|
||||
$item.getAttribute("data-hs-theme-click-value"),
|
||||
true,
|
||||
$item,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
$switchableThemes.forEach(($item) => {
|
||||
$item.addEventListener("change", (e) => {
|
||||
HSThemeAppearance.setAppearance(e.target.checked ? "dark" : "default");
|
||||
});
|
||||
|
||||
$item.checked = HSThemeAppearance.getAppearance() === "dark";
|
||||
});
|
||||
|
||||
window.addEventListener("on-hs-appearance-change", (e) => {
|
||||
$switchableThemes.forEach(($item) => {
|
||||
$item.checked = e.detail === "dark";
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
Loading…
Add table
Reference in a new issue