diff --git a/apps/www/package.json b/apps/www/package.json
index 739997e02..f14bd4ab0 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -61,6 +61,7 @@
"clsx": "^1.2.1",
"d3-scale": "^4.0.2",
"escape-html": "^1.0.3",
+ "esm-env": "^1.0.0",
"hast-util-to-html": "^8.0.4",
"lucide-svelte": "^0.221.0",
"mdast-util-to-string": "^3.2.0",
diff --git a/apps/www/pnpm-lock.yaml b/apps/www/pnpm-lock.yaml
index 5660cb2bc..f84189687 100644
--- a/apps/www/pnpm-lock.yaml
+++ b/apps/www/pnpm-lock.yaml
@@ -21,6 +21,9 @@ dependencies:
escape-html:
specifier: ^1.0.3
version: 1.0.3
+ esm-env:
+ specifier: ^1.0.0
+ version: 1.0.0
hast-util-to-html:
specifier: ^8.0.4
version: 8.0.4
@@ -1804,7 +1807,6 @@ packages:
/esm-env@1.0.0:
resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==}
- dev: true
/espree@9.5.2:
resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
diff --git a/apps/www/src/lib/components/docs/SiteHeader.svelte b/apps/www/src/lib/components/docs/SiteHeader.svelte
index c63505b79..81942cdcf 100644
--- a/apps/www/src/lib/components/docs/SiteHeader.svelte
+++ b/apps/www/src/lib/components/docs/SiteHeader.svelte
@@ -4,6 +4,7 @@
import { buttonVariants } from "$components/ui/button";
import { siteConfig } from "$lib/config/site";
import { cn } from "$lib/utils";
+ import LightSwitch from "./light-switch/LightSwitch.svelte";
import MobileNav from "./nav/MobileNav.svelte";
@@ -56,7 +57,7 @@
Twitter
-
+
diff --git a/apps/www/src/lib/components/docs/light-switch/LightSwitch.svelte b/apps/www/src/lib/components/docs/light-switch/LightSwitch.svelte
new file mode 100644
index 000000000..210eb6b34
--- /dev/null
+++ b/apps/www/src/lib/components/docs/light-switch/LightSwitch.svelte
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+ {#if $modeCurrent}
+
+ Dark
+ {:else}
+
+ Light
+ {/if}
+
+
diff --git a/apps/www/src/lib/components/docs/light-switch/light-switch.ts b/apps/www/src/lib/components/docs/light-switch/light-switch.ts
new file mode 100644
index 000000000..d742c935a
--- /dev/null
+++ b/apps/www/src/lib/components/docs/light-switch/light-switch.ts
@@ -0,0 +1,104 @@
+// Source: https://github.com/skeletonlabs/skeleton/blob/master/packages/skeleton/src/lib/utilities/LightSwitch/lightswitch.ts
+
+// Lightswitch Service
+
+import { get } from "svelte/store";
+// DO NOT replace this ⬇ import, it has to be imported directly
+import { localStorageStore } from "./local-storage-store";
+
+// Stores ---
+// TRUE: light, FALSE: dark
+
+/** Store: OS Preference Mode */
+export const modeOsPrefers = localStorageStore("modeOsPrefers", false);
+/** Store: User Preference Mode */
+export const modeUserPrefers = localStorageStore(
+ "modeUserPrefers",
+ undefined
+);
+/** Store: Current Mode State */
+export const modeCurrent = localStorageStore("modeCurrent", false);
+
+// Get ---
+
+/** Get the OS Preference for light/dark mode */
+export function getModeOsPrefers(): boolean {
+ const prefersLightMode = window.matchMedia(
+ "(prefers-color-scheme: light)"
+ ).matches;
+ modeOsPrefers.set(prefersLightMode);
+ return prefersLightMode;
+}
+
+/** Get the User for light/dark mode */
+export function getModeUserPrefers(): boolean | undefined {
+ return get(modeUserPrefers);
+}
+
+/** Get the Automatic Preference light/dark mode */
+export function getModeAutoPrefers(): boolean {
+ const os = getModeOsPrefers();
+ const user = getModeUserPrefers();
+ const modeValue = user !== undefined ? user : os;
+ return modeValue;
+}
+
+// Set ---
+
+/** Set the User Preference for light/dark mode */
+export function setModeUserPrefers(value: boolean): void {
+ modeUserPrefers.set(value);
+}
+
+/** Set the the current light/dark mode */
+export function setModeCurrent(value: boolean) {
+ const elemHtmlClasses = document.documentElement.classList;
+ const classDark = `dark`;
+ value === true
+ ? elemHtmlClasses.remove(classDark)
+ : elemHtmlClasses.add(classDark);
+ modeCurrent.set(value);
+}
+
+// Lightswitch Utility
+
+/** Set the visible light/dark mode on page load. */
+export function setInitialClassState() {
+ const elemHtmlClasses = document.documentElement.classList;
+ // Conditions
+ const condLocalStorageUserPrefs =
+ localStorage.getItem("modeUserPrefers") === "false";
+ const condLocalStorageUserPrefsExists = !(
+ "modeUserPrefers" in localStorage
+ );
+ const condMatchMedia = window.matchMedia(
+ "(prefers-color-scheme: dark)"
+ ).matches;
+ // Add/remove `.dark` class to HTML element
+ if (
+ condLocalStorageUserPrefs ||
+ (condLocalStorageUserPrefsExists && condMatchMedia)
+ ) {
+ elemHtmlClasses.add("dark");
+ } else {
+ elemHtmlClasses.remove("dark");
+ }
+}
+
+// Auto-Switch Utility
+
+/** Automatically set the visible light/dark, updates on change. */
+export function autoModeWatcher(): void {
+ const mql = window.matchMedia("(prefers-color-scheme: light)");
+ function setMode(value: boolean) {
+ const elemHtmlClasses = document.documentElement.classList;
+ const classDark = `dark`;
+ value === true
+ ? elemHtmlClasses.remove(classDark)
+ : elemHtmlClasses.add(classDark);
+ }
+ setMode(mql.matches);
+ mql.onchange = () => {
+ setMode(mql.matches);
+ };
+}
diff --git a/apps/www/src/lib/components/docs/light-switch/local-storage-store.ts b/apps/www/src/lib/components/docs/light-switch/local-storage-store.ts
new file mode 100644
index 000000000..7e2a63488
--- /dev/null
+++ b/apps/www/src/lib/components/docs/light-switch/local-storage-store.ts
@@ -0,0 +1,87 @@
+// Source: https://github.com/joshnuss/svelte-local-storage-store
+// https://github.com/joshnuss/svelte-local-storage-store/blob/master/index.ts
+// Represents version v0.4.0 (2023-01-18)
+import type { Writable } from "svelte/store";
+import { BROWSER } from "esm-env";
+import { get, writable as internal } from "svelte/store";
+
+declare type Updater = (value: T) => T;
+declare type StoreDict = { [key: string]: Writable };
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+const stores: StoreDict = {};
+
+interface Serializer {
+ parse(text: string): T;
+ stringify(object: T): string;
+}
+
+type StorageType = "local" | "session";
+
+interface Options {
+ serializer?: Serializer;
+ storage?: StorageType;
+}
+
+function getStorage(type: StorageType) {
+ return type === "local" ? localStorage : sessionStorage;
+}
+
+export function localStorageStore(
+ key: string,
+ initialValue: T,
+ options?: Options
+): Writable {
+ const serializer = options?.serializer ?? JSON;
+ const storageType = options?.storage ?? "local";
+
+ function updateStorage(key: string, value: T) {
+ if (!BROWSER) return;
+
+ getStorage(storageType).setItem(key, serializer.stringify(value));
+ }
+
+ if (!stores[key]) {
+ const store = internal(initialValue, (set) => {
+ const json = BROWSER ? getStorage(storageType).getItem(key) : null;
+
+ if (json) {
+ set(serializer.parse(json));
+ }
+
+ if (BROWSER) {
+ const handleStorage = (event: StorageEvent) => {
+ if (event.key === key)
+ set(
+ event.newValue
+ ? serializer.parse(event.newValue)
+ : null
+ );
+ };
+
+ window.addEventListener("storage", handleStorage);
+
+ return () =>
+ window.removeEventListener("storage", handleStorage);
+ }
+ });
+
+ const { subscribe, set } = store;
+
+ stores[key] = {
+ set(value: T) {
+ updateStorage(key, value);
+ set(value);
+ },
+ update(updater: Updater) {
+ const value = updater(get(store));
+
+ updateStorage(key, value);
+ set(value);
+ },
+ subscribe
+ };
+ }
+
+ return stores[key];
+}
diff --git a/apps/www/src/routes/+layout.svelte b/apps/www/src/routes/+layout.svelte
index 5a3eb68c9..e04702ff2 100644
--- a/apps/www/src/routes/+layout.svelte
+++ b/apps/www/src/routes/+layout.svelte
@@ -3,9 +3,14 @@
import SiteFooter from "$components/docs/SiteFooter.svelte";
import SiteHeader from "$components/docs/SiteHeader.svelte";
import TailwindIndicator from "$components/docs/TailwindIndicator.svelte";
+ import { setInitialClassState } from "$components/docs/light-switch/light-switch";
import "../styles/globals.css";
+
+ {@html ``}
+
+