diff --git a/src/lib/utilities/dark-mode/dark-mode.svelte b/src/lib/utilities/dark-mode/dark-mode.svelte
index 5b3ce499e3..c851c600a6 100644
--- a/src/lib/utilities/dark-mode/dark-mode.svelte
+++ b/src/lib/utilities/dark-mode/dark-mode.svelte
@@ -1,10 +1,14 @@
-
+
{@render children?.()}
diff --git a/src/lib/utilities/dark-mode/dark-mode.test.ts b/src/lib/utilities/dark-mode/dark-mode.test.ts
index 951baf8a52..48b81a449f 100644
--- a/src/lib/utilities/dark-mode/dark-mode.test.ts
+++ b/src/lib/utilities/dark-mode/dark-mode.test.ts
@@ -65,22 +65,73 @@ describe('dark-mode utilities', () => {
});
describe('darkMode', () => {
- it('should set data-theme to "dark" when dark mode is enabled', async () => {
+ it('should set data-theme to "dark" when dark mode is enabled', () => {
const node = document.createElement('div');
useDarkModePreference.set(true);
darkMode(node);
- await new Promise((resolve) => setTimeout(resolve, 0));
expect(node.dataset.theme).toBe('dark');
});
- it('should set data-theme to "light" when dark mode is disabled', async () => {
+ it('should set data-theme to "light" when dark mode is disabled', () => {
const node = document.createElement('div');
useDarkModePreference.set(false);
darkMode(node);
- await new Promise((resolve) => setTimeout(resolve, 0));
+
+ expect(node.dataset.theme).toBe('light');
+ });
+
+ it('should force "light" via overrideTheme even when dark mode is enabled', () => {
+ const node = document.createElement('div');
+ useDarkModePreference.set(true);
+
+ darkMode(node, 'light');
+
+ expect(node.dataset.theme).toBe('light');
+ });
+
+ it('should force "dark" via overrideTheme even when dark mode is disabled', () => {
+ const node = document.createElement('div');
+ useDarkModePreference.set(false);
+
+ darkMode(node, 'dark');
+
+ expect(node.dataset.theme).toBe('dark');
+ });
+
+ it('should re-apply the theme when the override changes via update', () => {
+ const node = document.createElement('div');
+ useDarkModePreference.set(true);
+
+ const action = darkMode(node, 'light');
+ expect(node.dataset.theme).toBe('light');
+
+ action.update('dark');
+ expect(node.dataset.theme).toBe('dark');
+ });
+
+ it('should fall back to the store theme when the override is cleared', () => {
+ const node = document.createElement('div');
+ useDarkModePreference.set(true);
+
+ const action = darkMode(node, 'light');
+ expect(node.dataset.theme).toBe('light');
+
+ action.update(undefined);
+ expect(node.dataset.theme).toBe('dark');
+ });
+
+ it('should stop updating data-theme after destroy', () => {
+ const node = document.createElement('div');
+ useDarkModePreference.set(false);
+
+ const action = darkMode(node);
+ expect(node.dataset.theme).toBe('light');
+
+ action.destroy();
+ useDarkModePreference.set(true);
expect(node.dataset.theme).toBe('light');
});
diff --git a/src/lib/utilities/dark-mode/dark-mode.ts b/src/lib/utilities/dark-mode/dark-mode.ts
index 710335fcc8..4b5637276d 100644
--- a/src/lib/utilities/dark-mode/dark-mode.ts
+++ b/src/lib/utilities/dark-mode/dark-mode.ts
@@ -3,6 +3,7 @@ import { derived } from 'svelte/store';
import { persistStore } from '$lib/stores/persist-store';
export type DarkModePreference = boolean | 'system';
+export type ThemeOverride = 'light' | 'dark';
export const useDarkModePreference = persistStore(
'dark mode',
@@ -25,12 +26,30 @@ export const useDarkMode = derived(useDarkModePreference, prefersDarkMode);
export const getNextDarkModePreference = (value: DarkModePreference) =>
value == 'system' ? true : value == true ? false : 'system';
-export const darkMode = (node: HTMLElement) => {
- useDarkMode.subscribe((value) => {
- if (value) {
- node.dataset.theme = 'dark';
- } else {
- node.dataset.theme = 'light';
- }
+const applyTheme = (
+ node: HTMLElement,
+ prefersDark: boolean,
+ overrideTheme?: ThemeOverride,
+) => {
+ node.dataset.theme = overrideTheme ?? (prefersDark ? 'dark' : 'light');
+};
+
+export const darkMode = (node: HTMLElement, overrideTheme?: ThemeOverride) => {
+ let override = overrideTheme;
+ let prefersDark = false;
+
+ const unsubscribe = useDarkMode.subscribe((value) => {
+ prefersDark = value;
+ applyTheme(node, prefersDark, override);
});
+
+ return {
+ update(newOverride?: ThemeOverride) {
+ override = newOverride;
+ applyTheme(node, prefersDark, override);
+ },
+ destroy() {
+ unsubscribe();
+ },
+ };
};
diff --git a/src/lib/utilities/dark-mode/index.ts b/src/lib/utilities/dark-mode/index.ts
index 49e5192a96..2e48089ad4 100644
--- a/src/lib/utilities/dark-mode/index.ts
+++ b/src/lib/utilities/dark-mode/index.ts
@@ -7,4 +7,4 @@ export {
getNextDarkModePreference,
} from './dark-mode';
-export type { DarkModePreference } from './dark-mode';
+export type { DarkModePreference, ThemeOverride } from './dark-mode';
diff --git a/src/routes/(login)/+layout.svelte b/src/routes/(login)/+layout.svelte
index 2b4a90763d..2b33c58c69 100644
--- a/src/routes/(login)/+layout.svelte
+++ b/src/routes/(login)/+layout.svelte
@@ -1,6 +1,8 @@
+
{@render children()}