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