@space-man/react-theme-animation 1.1.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,61 @@
1
- import React from 'react';
2
- type Theme = 'light' | 'dark' | 'system';
3
- export interface ViteThemeContextType {
1
+ import React, { ReactNode } from 'react';
2
+ import { Theme, ColorTheme, ThemeAnimationType } from '../types';
3
+ interface ViteThemeContextType {
4
+ ref: React.RefObject<HTMLButtonElement | null>;
4
5
  theme: Theme;
6
+ colorTheme: ColorTheme;
5
7
  resolvedTheme: 'light' | 'dark';
6
- setTheme: (theme: Theme) => void;
7
8
  systemTheme: 'light' | 'dark';
9
+ setTheme: (theme: Theme) => void;
10
+ setColorTheme: (colorTheme: ColorTheme) => void;
11
+ switchTheme: (theme: Theme, animationOff?: boolean) => Promise<void>;
12
+ switchColorTheme: (colorTheme: string) => void;
13
+ toggleTheme: (animationOff?: boolean) => Promise<void>;
14
+ toggleLightTheme: (animationOff?: boolean) => Promise<void>;
15
+ toggleDarkTheme: (animationOff?: boolean) => Promise<void>;
16
+ toggleColorTheme: () => void;
17
+ createColorThemeToggle: (targetColorTheme: string) => () => void;
18
+ isColorThemeActive: (targetColorTheme: string) => boolean;
19
+ switchThemeFromElement: (theme: Theme, element: HTMLButtonElement) => Promise<void>;
8
20
  }
9
21
  interface ViteThemeProviderProps {
10
- children: React.ReactNode;
11
- attribute?: 'class' | 'data-theme';
22
+ children: ReactNode;
23
+ themes?: Theme[];
24
+ colorThemes?: ColorTheme[];
12
25
  defaultTheme?: Theme;
13
- enableSystem?: boolean;
26
+ defaultColorTheme?: ColorTheme;
27
+ animationType?: ThemeAnimationType;
28
+ duration?: number;
29
+ attribute?: 'class' | 'data-theme';
14
30
  disableTransitionOnChange?: boolean;
15
31
  storageKey?: string;
32
+ colorStorageKey?: string;
33
+ globalClassName?: string;
34
+ colorThemePrefix?: string;
16
35
  }
36
+ /**
37
+ * Vite Theme Provider - Optimized for Vite SPA applications
38
+ *
39
+ * This provider is designed for Vite-based single-page applications with optional
40
+ * transition disabling to prevent flash during theme switches.
41
+ *
42
+ * Note: For flash prevention on initial load, add a script to your index.html
43
+ * or inject it via Vite plugin to apply theme before React loads.
44
+ *
45
+ * @param children - React children to wrap with theme context
46
+ * @param themes - Available theme options
47
+ * @param colorThemes - Available color theme options
48
+ * @param defaultTheme - Default theme to use
49
+ * @param defaultColorTheme - Default color theme to use
50
+ * @param animationType - Animation type for theme transitions
51
+ * @param duration - Animation duration in milliseconds
52
+ * @param attribute - HTML attribute to use for theme ('class' or 'data-theme')
53
+ * @param disableTransitionOnChange - Temporarily disable CSS transitions during theme switch
54
+ * @param storageKey - LocalStorage key for theme preference
55
+ * @param colorStorageKey - LocalStorage key for color theme preference
56
+ * @param globalClassName - Class name for dark theme (when using 'class' attribute)
57
+ * @param colorThemePrefix - Prefix for color theme classes
58
+ */
17
59
  export declare const ViteThemeProvider: React.FC<ViteThemeProviderProps>;
18
60
  export declare const useViteTheme: () => ViteThemeContextType;
19
61
  export {};
@@ -3,76 +3,131 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useViteTheme = exports.ViteThemeProvider = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
+ const use_theme_animation_1 = require("../hooks/use-theme-animation");
7
+ const types_1 = require("../types");
6
8
  const ViteThemeContext = (0, react_1.createContext)(undefined);
7
- const ViteThemeProvider = ({ children, attribute = 'class', defaultTheme = 'system', enableSystem = true, disableTransitionOnChange = false, storageKey = 'vite-theme', }) => {
8
- const [mounted, setMounted] = (0, react_1.useState)(false);
9
- const [systemTheme, setSystemTheme] = (0, react_1.useState)('light');
10
- const [theme, setThemeState] = (0, react_1.useState)(() => {
11
- if (typeof window === 'undefined')
12
- return defaultTheme;
13
- try {
14
- const savedTheme = localStorage.getItem(storageKey);
15
- return savedTheme && ['light', 'dark', 'system'].includes(savedTheme)
16
- ? savedTheme
17
- : defaultTheme;
18
- }
19
- catch {
20
- return defaultTheme;
21
- }
9
+ /**
10
+ * Vite Theme Provider - Optimized for Vite SPA applications
11
+ *
12
+ * This provider is designed for Vite-based single-page applications with optional
13
+ * transition disabling to prevent flash during theme switches.
14
+ *
15
+ * Note: For flash prevention on initial load, add a script to your index.html
16
+ * or inject it via Vite plugin to apply theme before React loads.
17
+ *
18
+ * @param children - React children to wrap with theme context
19
+ * @param themes - Available theme options
20
+ * @param colorThemes - Available color theme options
21
+ * @param defaultTheme - Default theme to use
22
+ * @param defaultColorTheme - Default color theme to use
23
+ * @param animationType - Animation type for theme transitions
24
+ * @param duration - Animation duration in milliseconds
25
+ * @param attribute - HTML attribute to use for theme ('class' or 'data-theme')
26
+ * @param disableTransitionOnChange - Temporarily disable CSS transitions during theme switch
27
+ * @param storageKey - LocalStorage key for theme preference
28
+ * @param colorStorageKey - LocalStorage key for color theme preference
29
+ * @param globalClassName - Class name for dark theme (when using 'class' attribute)
30
+ * @param colorThemePrefix - Prefix for color theme classes
31
+ */
32
+ const ViteThemeProvider = ({ children, themes = ['light', 'dark', 'system'], colorThemes = ['default'], defaultTheme = 'system', defaultColorTheme = 'default', animationType = types_1.ThemeAnimationType.CIRCLE, duration = 750, attribute = 'class', disableTransitionOnChange = false, storageKey = 'vite-theme', colorStorageKey = 'vite-color-theme', globalClassName = 'dark', colorThemePrefix = 'theme-', }) => {
33
+ const themeState = (0, use_theme_animation_1.useThemeAnimation)({
34
+ themes,
35
+ colorThemes,
36
+ defaultTheme,
37
+ defaultColorTheme,
38
+ animationType,
39
+ duration,
40
+ storageKey,
41
+ colorStorageKey,
42
+ globalClassName: attribute === 'class' ? globalClassName : undefined,
43
+ colorThemePrefix,
22
44
  });
23
- (0, react_1.useEffect)(() => {
24
- if (!enableSystem)
25
- return;
26
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
27
- const updateSystemTheme = () => {
28
- setSystemTheme(mediaQuery.matches ? 'dark' : 'light');
29
- };
30
- updateSystemTheme();
31
- mediaQuery.addEventListener('change', updateSystemTheme);
32
- return () => mediaQuery.removeEventListener('change', updateSystemTheme);
33
- }, [enableSystem]);
34
- const resolvedTheme = theme === 'system' ? systemTheme : theme;
35
- (0, react_1.useEffect)(() => {
36
- if (!mounted)
37
- return;
38
- const root = document.documentElement;
39
- if (disableTransitionOnChange) {
40
- const css = document.createElement('style');
41
- css.textContent = `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`;
42
- document.head.appendChild(css);
43
- window.getComputedStyle(document.body);
45
+ const applyTransitionDisable = (0, react_1.useCallback)(() => {
46
+ if (!disableTransitionOnChange)
47
+ return () => { };
48
+ const css = document.createElement('style');
49
+ css.textContent = `*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}`;
50
+ document.head.appendChild(css);
51
+ window.getComputedStyle(document.body);
52
+ return () => {
44
53
  setTimeout(() => {
45
54
  document.head.removeChild(css);
46
55
  }, 1);
47
- }
48
- if (attribute === 'class') {
49
- root.classList.remove('light', 'dark');
50
- root.classList.add(resolvedTheme);
56
+ };
57
+ }, [disableTransitionOnChange]);
58
+ const wrappedSetTheme = (0, react_1.useCallback)((theme) => {
59
+ const cleanup = applyTransitionDisable();
60
+ themeState.setTheme(theme);
61
+ cleanup();
62
+ }, [themeState.setTheme, applyTransitionDisable]);
63
+ const wrappedSwitchTheme = (0, react_1.useCallback)(async (theme, animationOff = false) => {
64
+ const cleanup = applyTransitionDisable();
65
+ await themeState.switchTheme(theme, animationOff);
66
+ cleanup();
67
+ }, [themeState.switchTheme, applyTransitionDisable]);
68
+ const wrappedToggleTheme = (0, react_1.useCallback)(async (animationOff = false) => {
69
+ const cleanup = applyTransitionDisable();
70
+ await themeState.toggleTheme(animationOff);
71
+ cleanup();
72
+ }, [themeState.toggleTheme, applyTransitionDisable]);
73
+ const wrappedToggleLightTheme = (0, react_1.useCallback)(async (animationOff = false) => {
74
+ const cleanup = applyTransitionDisable();
75
+ await themeState.toggleLightTheme(animationOff);
76
+ cleanup();
77
+ }, [themeState.toggleLightTheme, applyTransitionDisable]);
78
+ const wrappedToggleDarkTheme = (0, react_1.useCallback)(async (animationOff = false) => {
79
+ const cleanup = applyTransitionDisable();
80
+ await themeState.toggleDarkTheme(animationOff);
81
+ cleanup();
82
+ }, [themeState.toggleDarkTheme, applyTransitionDisable]);
83
+ const switchThemeFromElement = async (theme, element) => {
84
+ const cleanup = applyTransitionDisable();
85
+ if (themeState.ref.current) {
86
+ const originalRef = themeState.ref.current;
87
+ Object.defineProperty(themeState.ref, 'current', {
88
+ value: element,
89
+ writable: true,
90
+ configurable: true,
91
+ });
92
+ await themeState.switchTheme(theme);
93
+ Object.defineProperty(themeState.ref, 'current', {
94
+ value: originalRef,
95
+ writable: true,
96
+ configurable: true,
97
+ });
51
98
  }
52
99
  else {
53
- root.setAttribute(attribute, resolvedTheme);
54
- }
55
- try {
56
- localStorage.setItem(storageKey, theme);
100
+ Object.defineProperty(themeState.ref, 'current', {
101
+ value: element,
102
+ writable: true,
103
+ configurable: true,
104
+ });
105
+ await themeState.switchTheme(theme);
57
106
  }
58
- catch { }
59
- }, [theme, resolvedTheme, mounted, attribute, disableTransitionOnChange, storageKey]);
60
- (0, react_1.useEffect)(() => {
61
- setMounted(true);
62
- }, []);
63
- const setTheme = (0, react_1.useCallback)((newTheme) => {
64
- setThemeState(newTheme);
65
- }, []);
66
- if (!mounted) {
67
- return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
68
- }
69
- const value = {
70
- theme,
71
- resolvedTheme,
72
- setTheme,
107
+ cleanup();
108
+ };
109
+ const systemTheme = typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches
110
+ ? 'dark'
111
+ : 'light';
112
+ const contextValue = {
113
+ ref: themeState.ref,
114
+ theme: themeState.theme,
115
+ colorTheme: themeState.colorTheme,
116
+ resolvedTheme: themeState.resolvedTheme,
73
117
  systemTheme,
118
+ setTheme: wrappedSetTheme,
119
+ setColorTheme: themeState.setColorTheme,
120
+ switchTheme: wrappedSwitchTheme,
121
+ switchThemeFromElement,
122
+ switchColorTheme: themeState.switchColorTheme,
123
+ toggleTheme: wrappedToggleTheme,
124
+ toggleLightTheme: wrappedToggleLightTheme,
125
+ toggleDarkTheme: wrappedToggleDarkTheme,
126
+ toggleColorTheme: themeState.toggleColorTheme,
127
+ createColorThemeToggle: themeState.createColorThemeToggle,
128
+ isColorThemeActive: themeState.isColorThemeActive,
74
129
  };
75
- return (0, jsx_runtime_1.jsx)(ViteThemeContext.Provider, { value: value, children: children });
130
+ return (0, jsx_runtime_1.jsx)(ViteThemeContext.Provider, { value: contextValue, children: children });
76
131
  };
77
132
  exports.ViteThemeProvider = ViteThemeProvider;
78
133
  const useViteTheme = () => {
@@ -13,34 +13,23 @@ const useThemeAnimation = (props = {}) => {
13
13
  const isHighResolution = isBrowser && (window.innerWidth >= 3000 || window.innerHeight >= 2000);
14
14
  const duration = isHighResolution ? Math.max(propsDuration * 0.8, 500) : propsDuration;
15
15
  const [mounted, setMounted] = (0, react_1.useState)(false);
16
+ // Inject base styles once on mount
16
17
  (0, react_1.useEffect)(() => {
17
18
  (0, animations_1.injectBaseStyles)();
19
+ setMounted(true);
18
20
  }, []);
19
21
  const [internalTheme, setInternalTheme] = (0, react_1.useState)(() => {
20
22
  if (!isBrowser)
21
23
  return defaultTheme;
22
- return defaultTheme;
24
+ const saved = localStorage.getItem(storageKey);
25
+ return saved && themes.indexOf(saved) !== -1 ? saved : defaultTheme;
23
26
  });
24
27
  const [internalColorTheme, setInternalColorTheme] = (0, react_1.useState)(() => {
25
28
  if (!isBrowser)
26
29
  return defaultColorTheme;
27
- return defaultColorTheme;
30
+ const saved = localStorage.getItem(colorStorageKey);
31
+ return saved && colorThemes.indexOf(saved) !== -1 ? saved : defaultColorTheme;
28
32
  });
29
- (0, react_1.useEffect)(() => {
30
- setMounted(true);
31
- if (isBrowser && !externalTheme) {
32
- const saved = localStorage.getItem(storageKey);
33
- if (saved && themes.indexOf(saved) !== -1) {
34
- setInternalTheme(saved);
35
- }
36
- }
37
- if (isBrowser && !externalColorTheme) {
38
- const saved = localStorage.getItem(colorStorageKey);
39
- if (saved && colorThemes.indexOf(saved) !== -1) {
40
- setInternalColorTheme(saved);
41
- }
42
- }
43
- }, []);
44
33
  const currentTheme = externalTheme ?? internalTheme;
45
34
  const currentColorTheme = externalColorTheme ?? internalColorTheme;
46
35
  const [, setSystemTheme] = (0, react_1.useState)(() => (0, animations_1.getSystemTheme)());
@@ -97,8 +86,8 @@ const useThemeAnimation = (props = {}) => {
97
86
  onColorThemeChange(newColorTheme);
98
87
  }
99
88
  }, [onColorThemeChange, externalColorTheme, colorStorageKey]);
100
- const switchTheme = (0, react_1.useCallback)(async (newTheme) => {
101
- if (!ref.current || !(0, animations_1.supportsViewTransitions)() || (0, animations_1.prefersReducedMotion)()) {
89
+ const switchTheme = (0, react_1.useCallback)(async (newTheme, animationOff = false) => {
90
+ if (!ref.current || !(0, animations_1.supportsViewTransitions)() || (0, animations_1.prefersReducedMotion)() || animationOff) {
102
91
  setTheme(newTheme);
103
92
  return;
104
93
  }
@@ -186,26 +175,45 @@ const useThemeAnimation = (props = {}) => {
186
175
  slideToX,
187
176
  slideToY,
188
177
  ]);
189
- const toggleTheme = (0, react_1.useCallback)(async () => {
178
+ const switchColorTheme = (0, react_1.useCallback)((newColorTheme) => {
179
+ if (colorThemes.indexOf(newColorTheme) === -1) {
180
+ console.warn(`Color theme "${newColorTheme}" not found in available themes`);
181
+ return;
182
+ }
183
+ setColorTheme(newColorTheme);
184
+ }, [colorThemes, setColorTheme]);
185
+ const toggleTheme = (0, react_1.useCallback)(async (animationOff = false) => {
190
186
  const newTheme = resolvedTheme === 'dark' ? 'light' : 'dark';
191
- await switchTheme(newTheme);
187
+ await switchTheme(newTheme, animationOff);
192
188
  }, [resolvedTheme, switchTheme]);
193
- (0, react_1.useEffect)(() => {
194
- if (!isBrowser || !mounted)
189
+ const toggleLightTheme = (0, react_1.useCallback)(async (animationOff = false) => {
190
+ if (resolvedTheme === 'light')
195
191
  return;
196
- if (resolvedTheme === 'dark') {
197
- document.documentElement.classList.add(globalClassName);
198
- }
199
- else {
200
- document.documentElement.classList.remove(globalClassName);
201
- }
202
- colorThemes.forEach(theme => {
203
- document.documentElement.classList.remove(`${colorThemePrefix}${theme}`);
204
- });
205
- if (currentColorTheme !== 'default') {
206
- document.documentElement.classList.add(`${colorThemePrefix}${currentColorTheme}`);
207
- }
208
- }, [resolvedTheme, currentColorTheme, globalClassName, colorThemePrefix, colorThemes, mounted]);
192
+ await switchTheme('light', animationOff);
193
+ }, [resolvedTheme, switchTheme]);
194
+ const toggleDarkTheme = (0, react_1.useCallback)(async (animationOff = false) => {
195
+ if (resolvedTheme === 'dark')
196
+ return;
197
+ await switchTheme('dark', animationOff);
198
+ }, [resolvedTheme, switchTheme]);
199
+ const toggleColorTheme = (0, react_1.useCallback)(() => {
200
+ const currentIndex = colorThemes.indexOf(currentColorTheme);
201
+ const nextIndex = (currentIndex + 1) % colorThemes.length;
202
+ const nextColorTheme = colorThemes[nextIndex];
203
+ setColorTheme(nextColorTheme);
204
+ }, [currentColorTheme, colorThemes, setColorTheme]);
205
+ const createColorThemeToggle = (0, react_1.useCallback)((targetColorTheme) => {
206
+ return () => {
207
+ if (colorThemes.indexOf(targetColorTheme) === -1) {
208
+ console.warn(`Color theme "${targetColorTheme}" not found in available themes`);
209
+ return;
210
+ }
211
+ setColorTheme(targetColorTheme);
212
+ };
213
+ }, [colorThemes, setColorTheme]);
214
+ const isColorThemeActive = (0, react_1.useCallback)((targetColorTheme) => {
215
+ return currentColorTheme === targetColorTheme;
216
+ }, [currentColorTheme]);
209
217
  return {
210
218
  ref,
211
219
  theme: currentTheme,
@@ -213,8 +221,14 @@ const useThemeAnimation = (props = {}) => {
213
221
  resolvedTheme,
214
222
  setTheme,
215
223
  setColorTheme,
216
- toggleTheme,
217
224
  switchTheme,
225
+ switchColorTheme,
226
+ toggleTheme,
227
+ toggleLightTheme,
228
+ toggleDarkTheme,
229
+ toggleColorTheme,
230
+ createColorThemeToggle,
231
+ isColorThemeActive,
218
232
  };
219
233
  };
220
234
  exports.useThemeAnimation = useThemeAnimation;
@@ -2,8 +2,9 @@ export { useThemeAnimation } from './hooks/use-theme-animation';
2
2
  export { ThemeSwitcher } from './components/ThemeSwitcher';
3
3
  export { ThemeSelector } from './components/ThemeSelector';
4
4
  export { SpacemanThemeProvider, useSpacemanTheme } from './components/SpacemanThemeProvider';
5
+ export { NextThemeProvider, useNextTheme } from './components/NextThemeProvider';
6
+ export { TanStackThemeProvider, useTanStackTheme } from './components/TanStackThemeProvider';
5
7
  export { ViteThemeProvider, useViteTheme } from './components/ViteThemeProvider';
6
8
  export type { UseThemeAnimationProps, UseThemeAnimationReturn, ThemeSwitcherProps, ThemeSelectorProps, Theme, ColorTheme, SlideDirection, } from './types';
7
- export type { ViteThemeContextType } from './components/ViteThemeProvider';
8
9
  export { ThemeAnimationType } from './types';
9
10
  export { injectBaseStyles, getSystemTheme, resolveTheme, supportsViewTransitions, prefersReducedMotion, } from './utils/animations';
package/dist/cjs/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.injectBaseStyles = exports.ThemeAnimationType = exports.useViteTheme = exports.ViteThemeProvider = exports.useSpacemanTheme = exports.SpacemanThemeProvider = exports.ThemeSelector = exports.ThemeSwitcher = exports.useThemeAnimation = void 0;
3
+ exports.prefersReducedMotion = exports.supportsViewTransitions = exports.resolveTheme = exports.getSystemTheme = exports.injectBaseStyles = exports.ThemeAnimationType = exports.useViteTheme = exports.ViteThemeProvider = exports.useTanStackTheme = exports.TanStackThemeProvider = exports.useNextTheme = exports.NextThemeProvider = exports.useSpacemanTheme = exports.SpacemanThemeProvider = exports.ThemeSelector = exports.ThemeSwitcher = exports.useThemeAnimation = void 0;
4
4
  var use_theme_animation_1 = require("./hooks/use-theme-animation");
5
5
  Object.defineProperty(exports, "useThemeAnimation", { enumerable: true, get: function () { return use_theme_animation_1.useThemeAnimation; } });
6
6
  var ThemeSwitcher_1 = require("./components/ThemeSwitcher");
@@ -10,6 +10,13 @@ Object.defineProperty(exports, "ThemeSelector", { enumerable: true, get: functio
10
10
  var SpacemanThemeProvider_1 = require("./components/SpacemanThemeProvider");
11
11
  Object.defineProperty(exports, "SpacemanThemeProvider", { enumerable: true, get: function () { return SpacemanThemeProvider_1.SpacemanThemeProvider; } });
12
12
  Object.defineProperty(exports, "useSpacemanTheme", { enumerable: true, get: function () { return SpacemanThemeProvider_1.useSpacemanTheme; } });
13
+ // Framework-specific optimized providers
14
+ var NextThemeProvider_1 = require("./components/NextThemeProvider");
15
+ Object.defineProperty(exports, "NextThemeProvider", { enumerable: true, get: function () { return NextThemeProvider_1.NextThemeProvider; } });
16
+ Object.defineProperty(exports, "useNextTheme", { enumerable: true, get: function () { return NextThemeProvider_1.useNextTheme; } });
17
+ var TanStackThemeProvider_1 = require("./components/TanStackThemeProvider");
18
+ Object.defineProperty(exports, "TanStackThemeProvider", { enumerable: true, get: function () { return TanStackThemeProvider_1.TanStackThemeProvider; } });
19
+ Object.defineProperty(exports, "useTanStackTheme", { enumerable: true, get: function () { return TanStackThemeProvider_1.useTanStackTheme; } });
13
20
  var ViteThemeProvider_1 = require("./components/ViteThemeProvider");
14
21
  Object.defineProperty(exports, "ViteThemeProvider", { enumerable: true, get: function () { return ViteThemeProvider_1.ViteThemeProvider; } });
15
22
  Object.defineProperty(exports, "useViteTheme", { enumerable: true, get: function () { return ViteThemeProvider_1.useViteTheme; } });
@@ -38,8 +38,14 @@ export interface UseThemeAnimationReturn {
38
38
  resolvedTheme: 'light' | 'dark';
39
39
  setTheme: (theme: Theme) => void;
40
40
  setColorTheme: (colorTheme: ColorTheme) => void;
41
- toggleTheme: () => Promise<void>;
42
- switchTheme: (theme: Theme) => Promise<void>;
41
+ switchTheme: (theme: Theme, animationOff?: boolean) => Promise<void>;
42
+ switchColorTheme: (colorTheme: string) => void;
43
+ toggleTheme: (animationOff?: boolean) => Promise<void>;
44
+ toggleLightTheme: (animationOff?: boolean) => Promise<void>;
45
+ toggleDarkTheme: (animationOff?: boolean) => Promise<void>;
46
+ toggleColorTheme: () => void;
47
+ createColorThemeToggle: (targetColorTheme: string) => () => void;
48
+ isColorThemeActive: (targetColorTheme: string) => boolean;
43
49
  }
44
50
  export interface ThemeSwitcherProps {
45
51
  themes?: Theme[];
@@ -0,0 +1,81 @@
1
+ import React, { ReactNode } from 'react';
2
+ import { Theme, ColorTheme, ThemeAnimationType } from '../types';
3
+ /**
4
+ * Context type for the Next Theme Provider
5
+ */
6
+ interface NextThemeContextType {
7
+ ref: React.RefObject<HTMLButtonElement | null>;
8
+ theme: Theme;
9
+ colorTheme: ColorTheme;
10
+ resolvedTheme: 'light' | 'dark';
11
+ systemTheme: 'light' | 'dark';
12
+ setTheme: (theme: Theme) => void;
13
+ setColorTheme: (colorTheme: ColorTheme) => void;
14
+ switchTheme: (theme: Theme, animationOff?: boolean) => Promise<void>;
15
+ switchColorTheme: (colorTheme: string) => void;
16
+ toggleTheme: (animationOff?: boolean) => Promise<void>;
17
+ toggleLightTheme: (animationOff?: boolean) => Promise<void>;
18
+ toggleDarkTheme: (animationOff?: boolean) => Promise<void>;
19
+ toggleColorTheme: () => void;
20
+ createColorThemeToggle: (targetColorTheme: string) => () => void;
21
+ isColorThemeActive: (targetColorTheme: string) => boolean;
22
+ switchThemeFromElement: (theme: Theme, element: HTMLButtonElement) => Promise<void>;
23
+ }
24
+ /**
25
+ * Props for the Next Theme Provider
26
+ */
27
+ interface NextThemeProviderProps {
28
+ children: ReactNode;
29
+ themes?: Theme[];
30
+ colorThemes?: ColorTheme[];
31
+ defaultTheme?: Theme;
32
+ defaultColorTheme?: ColorTheme;
33
+ animationType?: ThemeAnimationType;
34
+ duration?: number;
35
+ storageKey?: string;
36
+ colorStorageKey?: string;
37
+ attribute?: 'class' | 'data-theme';
38
+ globalClassName?: string;
39
+ colorThemePrefix?: string;
40
+ nonce?: string;
41
+ disableAnimationOnInit?: boolean;
42
+ disablePreHydrationScript?: boolean;
43
+ }
44
+ /**
45
+ * Next Theme Provider - Next.js optimized theme management with animations
46
+ *
47
+ * This provider is specifically optimized for Next.js and SSR frameworks with
48
+ * pre-hydration script injection to prevent flash of wrong theme.
49
+ *
50
+ * Features:
51
+ * - Pre-hydration script for flash prevention (SSR apps)
52
+ * - Animated theme transitions via View Transitions API
53
+ * - Multi-dimensional theming (theme + colorTheme)
54
+ * - System theme detection
55
+ * - CSP support via nonce prop
56
+ *
57
+ * @param children - React children to wrap with theme context
58
+ * @param themes - Available theme options
59
+ * @param colorThemes - Available color theme options
60
+ * @param defaultTheme - Default theme to use
61
+ * @param defaultColorTheme - Default color theme to use
62
+ * @param animationType - Animation type for theme transitions
63
+ * @param duration - Animation duration in milliseconds
64
+ * @param storageKey - LocalStorage key for theme preference
65
+ * @param colorStorageKey - LocalStorage key for color theme preference
66
+ * @param attribute - HTML attribute to use for theme ('class' or 'data-theme')
67
+ * @param globalClassName - Class name for dark theme (when using 'class' attribute)
68
+ * @param colorThemePrefix - Prefix for color theme classes
69
+ * @param nonce - CSP nonce for inline script (SSR apps)
70
+ * @param disableAnimationOnInit - Skip animation during initial hydration
71
+ * @param disablePreHydrationScript - Disable flash prevention script (for SPAs without SSR)
72
+ */
73
+ export declare const NextThemeProvider: React.FC<NextThemeProviderProps>;
74
+ /**
75
+ * Hook to consume the Next Theme context
76
+ *
77
+ * @returns Theme context value with all theme state and methods
78
+ * @throws Error if used outside of NextThemeProvider
79
+ */
80
+ export declare const useNextTheme: () => NextThemeContextType;
81
+ export {};