Skip to content

Commit 8a5e4d3

Browse files
committed
Fix lightmode warning logic and code cleanup
1 parent 7ecfa2f commit 8a5e4d3

3 files changed

Lines changed: 65 additions & 54 deletions

File tree

src/lib/components/LightSwitch.svelte

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
import { buttonVariants } from '$lib/components/ui/button';
55
import * as Dialog from '$lib/components/ui/dialog';
66
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
7-
import {
8-
ColorSchemeStore,
9-
getDarkReaderState,
10-
willActivateLightMode,
11-
} from '$lib/stores/ColorSchemeStore';
7+
import { ColorSchemeStore, LightMode, getDarkReaderState } from '$lib/stores/ColorSchemeStore';
128
import { cn } from '$lib/utils';
139
import { toast } from 'svelte-sonner';
1410
15-
let pendingScheme = $state<'light' | 'dark' | 'system' | undefined>();
11+
let pendingScheme = $state<LightMode | undefined>();
1612
function handleOpenChanged(open: boolean) {
1713
if (open) return;
1814
pendingScheme = undefined;
@@ -22,8 +18,33 @@
2218
ColorSchemeStore.set(pendingScheme);
2319
pendingScheme = undefined;
2420
}
25-
function evaluateLightSwitch(scheme: 'light' | 'dark' | 'system') {
26-
if (willActivateLightMode(scheme) && scheme !== $ColorSchemeStore) {
21+
22+
// Decision matrix
23+
// light dark system pending
24+
// light false false false
25+
// dark true false check
26+
// system check false false
27+
// current
28+
function isGoingNuclear(current: LightMode, pending: LightMode) {
29+
// There will definetly be no transition from dark -> light
30+
if (current === pending || current === LightMode.Light || pending === LightMode.Dark)
31+
return false;
32+
33+
const currentDark = current === LightMode.Dark;
34+
const pendingLight = pending === LightMode.Light;
35+
if (currentDark && pendingLight) return true; // Change will definetly go from dark to light
36+
37+
// Now either current or pending is system, so we need to query the browser preference
38+
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
39+
// System prefers lightmode, if the current mode is dark the transition will happen
40+
return currentDark;
41+
}
42+
43+
// system prefers darkmode, if pending is light then the transition will happen
44+
return pendingLight;
45+
}
46+
function evaluateLightSwitch(scheme: LightMode) {
47+
if (isGoingNuclear($ColorSchemeStore, scheme)) {
2748
const darkreader = getDarkReaderState();
2849
if (darkreader.isActive) {
2950
toast.warning('DarkReader is enabled, activating light mode will have no effect!');
@@ -61,14 +82,11 @@
6182
<span class="sr-only">Toggle theme</span>
6283
</DropdownMenu.Trigger>
6384
<DropdownMenu.Content align="end">
64-
<DropdownMenu.Item class="cursor-pointer" onclick={() => evaluateLightSwitch('light')}
65-
>Light</DropdownMenu.Item
66-
>
67-
<DropdownMenu.Item class="cursor-pointer" onclick={() => evaluateLightSwitch('dark')}
68-
>Dark</DropdownMenu.Item
69-
>
70-
<DropdownMenu.Item class="cursor-pointer" onclick={() => evaluateLightSwitch('system')}
71-
>System</DropdownMenu.Item
72-
>
85+
{#each Object.values(LightMode) as value}
86+
<DropdownMenu.Item
87+
class="cursor-pointer capitalize"
88+
onclick={() => evaluateLightSwitch(value)}>{value}</DropdownMenu.Item
89+
>
90+
{/each}
7391
</DropdownMenu.Content>
7492
</DropdownMenu.Root>

src/lib/components/Turnstile.svelte

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
import { PUBLIC_TURNSTILE_DEV_BYPASS_VALUE, PUBLIC_TURNSTILE_SITE_KEY } from '$env/static/public';
55
import CloudflareLogo from '$lib/components/svg/CloudflareLogo.svelte';
66
import LoadingCircle from '$lib/components/svg/LoadingCircle.svelte';
7-
import { ColorSchemeStore } from '$lib/stores/ColorSchemeStore';
7+
import { ColorSchemeStore, LightMode } from '$lib/stores/ColorSchemeStore';
88
import type { TurnstileInstance } from '$lib/types/TurnstileInstance';
99
import { onMount } from 'svelte';
1010
import { toast } from 'svelte-sonner';
11+
import LightSwitch from './LightSwitch.svelte';
1112
1213
interface Props {
1314
action: string;
@@ -47,10 +48,9 @@
4748
setTimeout(resetWidget, 5000);
4849
}
4950
50-
let cfColorScheme = $derived($ColorSchemeStore === 'system' ? 'auto' : $ColorSchemeStore) as
51-
| 'dark'
52-
| 'light'
53-
| 'auto';
51+
let cfColorScheme = $derived(
52+
$ColorSchemeStore === LightMode.System ? 'auto' : $ColorSchemeStore
53+
) as 'dark' | 'light' | 'auto';
5454
5555
$effect(() => {
5656
if (!turnstile || !element || widgetState != 'mounted' || widgetId) return;

src/lib/stores/ColorSchemeStore.ts

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
import { browser } from '$app/environment';
22
import { type Updater, writable } from 'svelte/store';
33

4-
function getLocalStoreState() {
4+
export enum LightMode {
5+
Dark = 'dark',
6+
Light = 'light',
7+
System = 'system',
8+
}
9+
10+
function getLocalStoreState(): LightMode {
511
const scheme = localStorage.getItem('theme');
6-
if (scheme === 'dark' || scheme === 'light' || scheme === 'system') {
7-
return scheme;
12+
if (scheme !== null && Object.values(LightMode).includes(scheme as LightMode)) {
13+
return scheme as LightMode;
814
}
915

10-
localStorage.setItem('theme', 'system');
16+
localStorage.setItem('theme', LightMode.System);
1117

12-
return 'system';
18+
return LightMode.System;
1319
}
1420

1521
export function getDarkReaderState() {
1622
const rootHtml = document.documentElement;
1723

1824
const proxyInjected = rootHtml.getAttribute('data-darkreader-proxy-injected');
1925
const metaElement = rootHtml.querySelector('head meta[name="darkreader"]');
20-
21-
let scheme = rootHtml.getAttribute('data-darkreader-scheme');
22-
if (scheme === 'auto') {
23-
scheme = 'system';
24-
}
26+
const scheme = rootHtml.getAttribute('data-darkreader-scheme');
2527

2628
return {
2729
isInjected: proxyInjected === 'true',
@@ -30,58 +32,56 @@ export function getDarkReaderState() {
3032
};
3133
}
3234

33-
function getColorSchemePreference() {
35+
function getColorSchemePreference(): LightMode {
3436
// If we are not in a browser environment, return default
3537
if (!browser) {
36-
return 'system';
38+
return LightMode.System;
3739
}
3840

3941
// Check if local storage has a theme stored
4042
const localStoreState = getLocalStoreState();
41-
if (localStoreState !== 'system') {
43+
if (localStoreState !== LightMode.System) {
4244
return localStoreState;
4345
}
4446

4547
// If a user has Dark Reader extension installed, assume they prefer dark mode
4648
const darkReaderState = getDarkReaderState();
4749
if (darkReaderState.isInjected) {
48-
return 'dark';
50+
return LightMode.Dark;
4951
}
5052

5153
// Check if the user has a system theme preference for light mode
5254
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
53-
return 'light';
55+
return LightMode.Light;
5456
}
5557

5658
// Default to dark mode
57-
return 'dark';
59+
return LightMode.Dark;
5860
}
5961

60-
const { set, update, subscribe } = writable<'dark' | 'light' | 'system'>(
61-
getColorSchemePreference()
62-
);
62+
const { set, update, subscribe } = writable<LightMode>(getColorSchemePreference());
6363

6464
function setHtmlDarkModeSelector(value: boolean) {
6565
document.documentElement.classList.toggle('dark', value);
6666
}
6767

6868
function handleSchemePreferenceChange() {
6969
const scheme = getColorSchemePreference();
70-
setHtmlDarkModeSelector(scheme === 'dark');
70+
setHtmlDarkModeSelector(scheme === LightMode.Dark);
7171
}
7272

7373
export const ColorSchemeStore = {
74-
set: (value: 'dark' | 'light' | 'system') => {
74+
set: (value: LightMode) => {
7575
localStorage.setItem('theme', value);
7676
set(value);
7777
handleSchemePreferenceChange();
7878
},
79-
update: (updater: Updater<'dark' | 'light' | 'system'>) => {
79+
update: (updater: Updater<LightMode>) => {
8080
update((value) => {
8181
const oldValue = value;
8282
const newValue = updater(value);
8383
if (oldValue !== newValue) {
84-
setHtmlDarkModeSelector(newValue === 'dark');
84+
setHtmlDarkModeSelector(newValue === LightMode.Dark);
8585
localStorage.setItem('theme', newValue);
8686
}
8787
return newValue;
@@ -90,16 +90,9 @@ export const ColorSchemeStore = {
9090
subscribe,
9191
};
9292

93-
export function willActivateLightMode(value: 'dark' | 'light' | 'system') {
94-
return (
95-
value === 'light' ||
96-
(value === 'system' && window.matchMedia('(prefers-color-scheme: light)').matches)
97-
);
98-
}
99-
10093
export function initializeDarkModeStore() {
10194
const schemePreference = getColorSchemePreference();
102-
setHtmlDarkModeSelector(schemePreference === 'dark');
95+
setHtmlDarkModeSelector(schemePreference === LightMode.Dark);
10396

10497
window
10598
.matchMedia('(prefers-color-scheme: light)')
@@ -111,6 +104,6 @@ export function initializeDarkModeStore() {
111104
window.addEventListener('storage', (event) => {
112105
if (event.key !== 'theme') return;
113106

114-
setHtmlDarkModeSelector(event.newValue === 'dark');
107+
setHtmlDarkModeSelector(event.newValue === LightMode.Dark);
115108
});
116109
}

0 commit comments

Comments
 (0)