start working version of new theme handler

This commit is contained in:
Jesse Winton
2025-04-04 13:22:10 -04:00
parent bbc73067f5
commit dcf6c1f693
6 changed files with 46 additions and 42 deletions

View File

@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
import Select, { type SelectOption } from './Select.svelte'; import Select, { type SelectOption } from './Select.svelte';
import { Select as Select2 } from '$lib/components/ui'; import { Select as Select2 } from '$lib/components/ui';
import { themeStore, setTheme, type Theme } from '$lib/providers/theme'; import { themeStore, setTheme } from '$lib/providers/theme';
import { get } from 'svelte/store';
const options = [ const options = [
{ {
@@ -28,6 +27,6 @@
{options} {options}
bind:value={$themeStore.theme} bind:value={$themeStore.theme}
onValueChange={(e) => { onValueChange={(e) => {
setTheme(e as Theme); setTheme(e);
}} }}
/> />

View File

@@ -37,7 +37,7 @@
<div {...select.content}> <div {...select.content}>
{#each options as option} {#each options as option}
<div {...select.getOption(option.value)}> <div {...select.getOption(option.value)}>
{option} {option.label}
</div> </div>
{/each} {/each}
</div> </div>

View File

@@ -1,25 +1,34 @@
import { writable } from 'svelte/store';
export type Theme = 'dark' | 'light' | 'system'; import { writable } from 'svelte/store';
export interface ThemeStore { export interface ThemeStore {
themes: string[]; themes: string[];
forcedTheme?: Theme; forcedTheme?: string;
theme?: Theme; theme?: string;
resolvedTheme?: string; resolvedTheme?: string;
systemTheme?: Omit<Theme, 'system'>; systemTheme?: string;
} }
export const setTheme = (theme?: Theme): void => // Create the theme store
themeStore.update((store) => ({ ...store, theme })); export const themeStore = writable<ThemeStore>({
themes: ['light', 'dark'],
export const themeStore = writable<ThemeStore>({ forcedTheme: undefined,
themes: [], theme: undefined,
forcedTheme: undefined, resolvedTheme: undefined,
theme: undefined, systemTheme: undefined
resolvedTheme: undefined, });
systemTheme: undefined
});
export { default as ThemeProvider } from './theme.svelte' export const setTheme = (theme?: string) =>
themeStore.update((store) => ({ ...store, theme }));
export const setResolvedTheme = (resolvedTheme?: string) =>
themeStore.update((store) => ({ ...store, resolvedTheme }));
export const setSystemTheme = (systemTheme?: string) =>
themeStore.update((store) => ({ ...store, systemTheme }));
export const setThemes = (themes: string[]) =>
themeStore.update((store) => ({ ...store, themes }));
export { default as ThemeProvider } from './theme.svelte';

View File

@@ -1,19 +1,19 @@
<script lang="ts"> <script lang="ts">
import { colorSchemes, MEDIA } from './constants'; import { colorSchemes, MEDIA } from './constants';
import { disableAnimation, getSystemTheme, getTheme } from './helpers'; import { disableAnimation, getSystemTheme, getTheme } from './helpers';
import { themeStore, setTheme, type Theme } from './index'; import { themeStore, setTheme } from './index';
import ThemeScript from './theme-script.svelte'; import ThemeScript from './theme-script.svelte';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
interface Props { interface Props {
forcedTheme?: Theme; forcedTheme?: string;
disableTransitionOnChange?: boolean; disableTransitionOnChange?: boolean;
enableSystem?: boolean; enableSystem?: boolean;
enableColorScheme?: boolean; enableColorScheme?: boolean;
storageKey?: string; storageKey?: string;
themes?: string[]; themes?: string[];
defaultTheme?: Theme; defaultTheme?: string;
attribute?: string | 'class'; attribute?: string | 'class';
value?: { value?: {
[themeName: string]: string; [themeName: string]: string;
@@ -51,13 +51,14 @@
const attrs = !value ? themes : Object.values(value); const attrs = !value ? themes : Object.values(value);
const handleMediaQuery = (e?: MediaQueryList) => { const handleMediaQuery = (e?: MediaQueryList) => {
const systemTheme = getSystemTheme(e) as Theme; const systemTheme = getSystemTheme(e) as string;
$themeStore.resolvedTheme = systemTheme; $themeStore.resolvedTheme = systemTheme;
$themeStore.systemTheme = systemTheme;
if (theme === 'system' && !forcedTheme) changeTheme(systemTheme, false); if (theme === 'system' && !forcedTheme) changeTheme(systemTheme, false, true);
}; };
const changeTheme = (theme?: Theme, updateStorage?: boolean, updateDOM?: boolean) => { const changeTheme = (theme?: string, updateStorage?: boolean, updateDOM?: boolean) => {
if (!theme) return; if (!theme) return;
let name = value?.[theme] || theme; let name = value?.[theme] || theme;
@@ -77,7 +78,7 @@
} }
if (updateDOM && browser) { if (updateDOM && browser) {
const d = document.documentElement; const d = document.body;
if (attribute === 'class') { if (attribute === 'class') {
d.classList.remove(...(attrs as string[])); d.classList.remove(...(attrs as string[]));
@@ -93,20 +94,20 @@
const storageHandler = (e: StorageEvent) => { const storageHandler = (e: StorageEvent) => {
if (e.key !== storageKey) return; if (e.key !== storageKey) return;
setTheme((e.newValue as Theme) || (defaultTheme as Theme)); setTheme((e.newValue as string) || (defaultTheme as string));
}; };
const onWindow = (window: Window) => { const onWindow = (window: Window) => {
const media = window.matchMedia(MEDIA); const media = window.matchMedia(MEDIA);
// Intentionally use deprecated listener methods to support iOS & old browsers // Use modern event listener approach
media.addListener(mediaHandler); media.addEventListener('change', mediaHandler);
mediaHandler(media); mediaHandler(media);
window.addEventListener('storage', storageHandler); window.addEventListener('storage', storageHandler);
return { return {
destroy() { destroy() {
window.removeEventListener('storage', storageHandler); window.removeEventListener('storage', storageHandler);
media.removeListener(mediaHandler); media.removeEventListener('change', mediaHandler);
} }
}; };
}; };
@@ -133,7 +134,7 @@
if (forcedTheme) { if (forcedTheme) {
changeTheme(theme, true, false); changeTheme(theme, true, false);
} else { } else {
changeTheme(theme); changeTheme(theme, true, true); // Add true for updateDOM parameter
} }
}); });
</script> </script>

View File

@@ -132,7 +132,7 @@
> >
<slot /> <slot />
<ThemeProvider /> <ThemeProvider attribute="class" disableTransitionOnChange />
<style lang="scss"> <style lang="scss">
:global(html) { :global(html) {

View File

@@ -1,5 +0,0 @@
import { Config } from 'tailwindcss';
const config = {} satisfies Config;
export default config;