react-native-zustand-theme 1.0.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.
@@ -0,0 +1,176 @@
1
+ import * as zustand_middleware from 'zustand/middleware';
2
+ import * as zustand from 'zustand';
3
+ import React, { ReactNode } from 'react';
4
+
5
+ declare const lightTheme: {
6
+ readonly colors: {
7
+ readonly background: "#ffffff";
8
+ readonly surface: "#f8f8f8";
9
+ readonly card: "#ffffff";
10
+ readonly border: "#d1d1d1";
11
+ readonly inverted: "#000";
12
+ readonly invertedMuted: "#333333";
13
+ readonly invertedExtraMuted: "#ededed";
14
+ readonly boxShadow: "rgba(0, 0, 0, 0.25)";
15
+ readonly textPrimary: "#000000";
16
+ readonly textSecondary: "#666666";
17
+ readonly textMuted: "#999999";
18
+ readonly accent: "#26702A";
19
+ readonly accentText: "#ffffff";
20
+ readonly success: "#2ecc71";
21
+ readonly warning: "#f39c12";
22
+ readonly info: "#3498db";
23
+ readonly danger: "#e74c3c";
24
+ };
25
+ readonly radius: {
26
+ readonly sm: 6;
27
+ readonly md: 12;
28
+ readonly lg: 20;
29
+ readonly xl: 24;
30
+ };
31
+ readonly spacing: {
32
+ readonly xs: 4;
33
+ readonly sm: 8;
34
+ readonly md: 16;
35
+ readonly lg: 20;
36
+ readonly xl: 24;
37
+ };
38
+ readonly fontSize: {
39
+ readonly sm: 12;
40
+ readonly md: 14;
41
+ readonly lg: 16;
42
+ readonly xl: 18;
43
+ readonly xxl: 22;
44
+ readonly xxxl: 26;
45
+ readonly xxxxl: 32;
46
+ };
47
+ readonly fontWeight: {
48
+ readonly regular: "400";
49
+ readonly medium: "600";
50
+ readonly bold: "700";
51
+ };
52
+ };
53
+ type LightTheme = typeof lightTheme;
54
+
55
+ declare const darkTheme: {
56
+ readonly colors: {
57
+ readonly background: "#191919";
58
+ readonly surface: "#222222";
59
+ readonly card: "#1f1f1f";
60
+ readonly border: "#3a3a3a";
61
+ readonly inverted: "#fff";
62
+ readonly invertedMuted: "#333333";
63
+ readonly invertedExtraMuted: "#2e2e2e";
64
+ readonly boxShadow: "rgba(255,255,255,0.13)";
65
+ readonly textPrimary: "#f5f5f5";
66
+ readonly textSecondary: "#b3b3b3";
67
+ readonly textMuted: "#9a9a9a";
68
+ readonly accent: "#26702A";
69
+ readonly accentText: "#ffffff";
70
+ readonly success: "#2ecc71";
71
+ readonly warning: "#f5b041";
72
+ readonly info: "#5dade2";
73
+ readonly danger: "#ec7063";
74
+ };
75
+ readonly radius: {
76
+ readonly sm: 6;
77
+ readonly md: 12;
78
+ readonly lg: 20;
79
+ readonly xl: 24;
80
+ };
81
+ readonly spacing: {
82
+ readonly xs: 4;
83
+ readonly sm: 8;
84
+ readonly md: 16;
85
+ readonly lg: 20;
86
+ readonly xl: 24;
87
+ };
88
+ readonly fontSize: {
89
+ readonly sm: 12;
90
+ readonly md: 14;
91
+ readonly lg: 16;
92
+ readonly xl: 18;
93
+ readonly xxl: 22;
94
+ readonly xxxl: 26;
95
+ readonly xxxxl: 32;
96
+ };
97
+ readonly fontWeight: {
98
+ readonly regular: "400";
99
+ readonly medium: "600";
100
+ readonly bold: "700";
101
+ };
102
+ };
103
+ type DarkTheme = typeof darkTheme;
104
+
105
+ declare const baseTokens: {
106
+ readonly radius: {
107
+ readonly sm: 6;
108
+ readonly md: 12;
109
+ readonly lg: 20;
110
+ readonly xl: 24;
111
+ };
112
+ readonly spacing: {
113
+ readonly xs: 4;
114
+ readonly sm: 8;
115
+ readonly md: 16;
116
+ readonly lg: 20;
117
+ readonly xl: 24;
118
+ };
119
+ readonly fontSize: {
120
+ readonly sm: 12;
121
+ readonly md: 14;
122
+ readonly lg: 16;
123
+ readonly xl: 18;
124
+ readonly xxl: 22;
125
+ readonly xxxl: 26;
126
+ readonly xxxxl: 32;
127
+ };
128
+ readonly fontWeight: {
129
+ readonly regular: "400";
130
+ readonly medium: "600";
131
+ readonly bold: "700";
132
+ };
133
+ };
134
+ type BaseTokens = typeof baseTokens;
135
+
136
+ type ThemeMode = 'light' | 'dark';
137
+ type Theme = LightTheme | DarkTheme;
138
+ interface ThemeState {
139
+ mode: ThemeMode;
140
+ theme: Theme;
141
+ _hasHydrated: boolean;
142
+ setHasHydrated: (state: boolean) => void;
143
+ toggleMode: () => void;
144
+ setMode: (mode: ThemeMode) => void;
145
+ }
146
+ declare function registerThemeResolver(resolver: (mode: ThemeMode) => Theme): void;
147
+ declare const useThemeStore: zustand.UseBoundStore<Omit<zustand.StoreApi<ThemeState>, "setState" | "persist"> & {
148
+ setState(partial: ThemeState | Partial<ThemeState> | ((state: ThemeState) => ThemeState | Partial<ThemeState>), replace?: false | undefined): unknown;
149
+ setState(state: ThemeState | ((state: ThemeState) => ThemeState), replace: true): unknown;
150
+ persist: {
151
+ setOptions: (options: Partial<zustand_middleware.PersistOptions<ThemeState, unknown, unknown>>) => void;
152
+ clearStorage: () => void;
153
+ rehydrate: () => Promise<void> | void;
154
+ hasHydrated: () => boolean;
155
+ onHydrate: (fn: (state: ThemeState) => void) => () => void;
156
+ onFinishHydration: (fn: (state: ThemeState) => void) => () => void;
157
+ getOptions: () => Partial<zustand_middleware.PersistOptions<ThemeState, unknown, unknown>>;
158
+ };
159
+ }>;
160
+
161
+ declare function useTheme(): {
162
+ theme: Theme;
163
+ mode: ThemeMode;
164
+ toggleMode: () => void;
165
+ setMode: (mode: ThemeMode) => void;
166
+ isDark: boolean;
167
+ };
168
+
169
+ interface ThemeProviderProps {
170
+ children: ReactNode;
171
+ /** Custom loading fallback while AsyncStorage hydrates */
172
+ fallback?: ReactNode;
173
+ }
174
+ declare function ThemeProvider({ children, fallback }: ThemeProviderProps): React.JSX.Element;
175
+
176
+ export { type BaseTokens, type DarkTheme, type LightTheme, type Theme, type ThemeMode, ThemeProvider, type ThemeState, baseTokens, darkTheme, lightTheme, registerThemeResolver, useTheme, useThemeStore };
@@ -0,0 +1,176 @@
1
+ import * as zustand_middleware from 'zustand/middleware';
2
+ import * as zustand from 'zustand';
3
+ import React, { ReactNode } from 'react';
4
+
5
+ declare const lightTheme: {
6
+ readonly colors: {
7
+ readonly background: "#ffffff";
8
+ readonly surface: "#f8f8f8";
9
+ readonly card: "#ffffff";
10
+ readonly border: "#d1d1d1";
11
+ readonly inverted: "#000";
12
+ readonly invertedMuted: "#333333";
13
+ readonly invertedExtraMuted: "#ededed";
14
+ readonly boxShadow: "rgba(0, 0, 0, 0.25)";
15
+ readonly textPrimary: "#000000";
16
+ readonly textSecondary: "#666666";
17
+ readonly textMuted: "#999999";
18
+ readonly accent: "#26702A";
19
+ readonly accentText: "#ffffff";
20
+ readonly success: "#2ecc71";
21
+ readonly warning: "#f39c12";
22
+ readonly info: "#3498db";
23
+ readonly danger: "#e74c3c";
24
+ };
25
+ readonly radius: {
26
+ readonly sm: 6;
27
+ readonly md: 12;
28
+ readonly lg: 20;
29
+ readonly xl: 24;
30
+ };
31
+ readonly spacing: {
32
+ readonly xs: 4;
33
+ readonly sm: 8;
34
+ readonly md: 16;
35
+ readonly lg: 20;
36
+ readonly xl: 24;
37
+ };
38
+ readonly fontSize: {
39
+ readonly sm: 12;
40
+ readonly md: 14;
41
+ readonly lg: 16;
42
+ readonly xl: 18;
43
+ readonly xxl: 22;
44
+ readonly xxxl: 26;
45
+ readonly xxxxl: 32;
46
+ };
47
+ readonly fontWeight: {
48
+ readonly regular: "400";
49
+ readonly medium: "600";
50
+ readonly bold: "700";
51
+ };
52
+ };
53
+ type LightTheme = typeof lightTheme;
54
+
55
+ declare const darkTheme: {
56
+ readonly colors: {
57
+ readonly background: "#191919";
58
+ readonly surface: "#222222";
59
+ readonly card: "#1f1f1f";
60
+ readonly border: "#3a3a3a";
61
+ readonly inverted: "#fff";
62
+ readonly invertedMuted: "#333333";
63
+ readonly invertedExtraMuted: "#2e2e2e";
64
+ readonly boxShadow: "rgba(255,255,255,0.13)";
65
+ readonly textPrimary: "#f5f5f5";
66
+ readonly textSecondary: "#b3b3b3";
67
+ readonly textMuted: "#9a9a9a";
68
+ readonly accent: "#26702A";
69
+ readonly accentText: "#ffffff";
70
+ readonly success: "#2ecc71";
71
+ readonly warning: "#f5b041";
72
+ readonly info: "#5dade2";
73
+ readonly danger: "#ec7063";
74
+ };
75
+ readonly radius: {
76
+ readonly sm: 6;
77
+ readonly md: 12;
78
+ readonly lg: 20;
79
+ readonly xl: 24;
80
+ };
81
+ readonly spacing: {
82
+ readonly xs: 4;
83
+ readonly sm: 8;
84
+ readonly md: 16;
85
+ readonly lg: 20;
86
+ readonly xl: 24;
87
+ };
88
+ readonly fontSize: {
89
+ readonly sm: 12;
90
+ readonly md: 14;
91
+ readonly lg: 16;
92
+ readonly xl: 18;
93
+ readonly xxl: 22;
94
+ readonly xxxl: 26;
95
+ readonly xxxxl: 32;
96
+ };
97
+ readonly fontWeight: {
98
+ readonly regular: "400";
99
+ readonly medium: "600";
100
+ readonly bold: "700";
101
+ };
102
+ };
103
+ type DarkTheme = typeof darkTheme;
104
+
105
+ declare const baseTokens: {
106
+ readonly radius: {
107
+ readonly sm: 6;
108
+ readonly md: 12;
109
+ readonly lg: 20;
110
+ readonly xl: 24;
111
+ };
112
+ readonly spacing: {
113
+ readonly xs: 4;
114
+ readonly sm: 8;
115
+ readonly md: 16;
116
+ readonly lg: 20;
117
+ readonly xl: 24;
118
+ };
119
+ readonly fontSize: {
120
+ readonly sm: 12;
121
+ readonly md: 14;
122
+ readonly lg: 16;
123
+ readonly xl: 18;
124
+ readonly xxl: 22;
125
+ readonly xxxl: 26;
126
+ readonly xxxxl: 32;
127
+ };
128
+ readonly fontWeight: {
129
+ readonly regular: "400";
130
+ readonly medium: "600";
131
+ readonly bold: "700";
132
+ };
133
+ };
134
+ type BaseTokens = typeof baseTokens;
135
+
136
+ type ThemeMode = 'light' | 'dark';
137
+ type Theme = LightTheme | DarkTheme;
138
+ interface ThemeState {
139
+ mode: ThemeMode;
140
+ theme: Theme;
141
+ _hasHydrated: boolean;
142
+ setHasHydrated: (state: boolean) => void;
143
+ toggleMode: () => void;
144
+ setMode: (mode: ThemeMode) => void;
145
+ }
146
+ declare function registerThemeResolver(resolver: (mode: ThemeMode) => Theme): void;
147
+ declare const useThemeStore: zustand.UseBoundStore<Omit<zustand.StoreApi<ThemeState>, "setState" | "persist"> & {
148
+ setState(partial: ThemeState | Partial<ThemeState> | ((state: ThemeState) => ThemeState | Partial<ThemeState>), replace?: false | undefined): unknown;
149
+ setState(state: ThemeState | ((state: ThemeState) => ThemeState), replace: true): unknown;
150
+ persist: {
151
+ setOptions: (options: Partial<zustand_middleware.PersistOptions<ThemeState, unknown, unknown>>) => void;
152
+ clearStorage: () => void;
153
+ rehydrate: () => Promise<void> | void;
154
+ hasHydrated: () => boolean;
155
+ onHydrate: (fn: (state: ThemeState) => void) => () => void;
156
+ onFinishHydration: (fn: (state: ThemeState) => void) => () => void;
157
+ getOptions: () => Partial<zustand_middleware.PersistOptions<ThemeState, unknown, unknown>>;
158
+ };
159
+ }>;
160
+
161
+ declare function useTheme(): {
162
+ theme: Theme;
163
+ mode: ThemeMode;
164
+ toggleMode: () => void;
165
+ setMode: (mode: ThemeMode) => void;
166
+ isDark: boolean;
167
+ };
168
+
169
+ interface ThemeProviderProps {
170
+ children: ReactNode;
171
+ /** Custom loading fallback while AsyncStorage hydrates */
172
+ fallback?: ReactNode;
173
+ }
174
+ declare function ThemeProvider({ children, fallback }: ThemeProviderProps): React.JSX.Element;
175
+
176
+ export { type BaseTokens, type DarkTheme, type LightTheme, type Theme, type ThemeMode, ThemeProvider, type ThemeState, baseTokens, darkTheme, lightTheme, registerThemeResolver, useTheme, useThemeStore };
package/dist/index.js ADDED
@@ -0,0 +1,158 @@
1
+ 'use strict';
2
+
3
+ var zustand = require('zustand');
4
+ var middleware = require('zustand/middleware');
5
+ var AsyncStorage = require('@react-native-async-storage/async-storage');
6
+ var reactNative = require('react-native');
7
+ var React = require('react');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var AsyncStorage__default = /*#__PURE__*/_interopDefault(AsyncStorage);
12
+ var React__default = /*#__PURE__*/_interopDefault(React);
13
+
14
+ // src/tokens.ts
15
+ var baseTokens = {
16
+ radius: {
17
+ sm: 6,
18
+ md: 12,
19
+ lg: 20,
20
+ xl: 24
21
+ },
22
+ spacing: {
23
+ xs: 4,
24
+ sm: 8,
25
+ md: 16,
26
+ lg: 20,
27
+ xl: 24
28
+ },
29
+ fontSize: {
30
+ sm: 12,
31
+ md: 14,
32
+ lg: 16,
33
+ xl: 18,
34
+ xxl: 22,
35
+ xxxl: 26,
36
+ xxxxl: 32
37
+ },
38
+ fontWeight: {
39
+ regular: "400",
40
+ medium: "600",
41
+ bold: "700"
42
+ }
43
+ };
44
+
45
+ // src/lightTheme.ts
46
+ var lightTheme = {
47
+ ...baseTokens,
48
+ colors: {
49
+ background: "#ffffff",
50
+ surface: "#f8f8f8",
51
+ card: "#ffffff",
52
+ border: "#d1d1d1",
53
+ inverted: "#000",
54
+ invertedMuted: "#333333",
55
+ invertedExtraMuted: "#ededed",
56
+ boxShadow: "rgba(0, 0, 0, 0.25)",
57
+ textPrimary: "#000000",
58
+ textSecondary: "#666666",
59
+ textMuted: "#999999",
60
+ accent: "#26702A",
61
+ accentText: "#ffffff",
62
+ success: "#2ecc71",
63
+ warning: "#f39c12",
64
+ info: "#3498db",
65
+ danger: "#e74c3c"
66
+ }
67
+ };
68
+
69
+ // src/darkTheme.ts
70
+ var darkTheme = {
71
+ ...baseTokens,
72
+ colors: {
73
+ background: "#191919",
74
+ surface: "#222222",
75
+ card: "#1f1f1f",
76
+ border: "#3a3a3a",
77
+ inverted: "#fff",
78
+ invertedMuted: "#333333",
79
+ invertedExtraMuted: "#2e2e2e",
80
+ boxShadow: "rgba(255,255,255,0.13)",
81
+ textPrimary: "#f5f5f5",
82
+ textSecondary: "#b3b3b3",
83
+ textMuted: "#9a9a9a",
84
+ accent: "#26702A",
85
+ accentText: "#ffffff",
86
+ success: "#2ecc71",
87
+ warning: "#f5b041",
88
+ info: "#5dade2",
89
+ danger: "#ec7063"
90
+ }
91
+ };
92
+ var _a;
93
+ var systemScheme = (_a = reactNative.Appearance.getColorScheme()) != null ? _a : "light";
94
+ var customResolver = null;
95
+ function registerThemeResolver(resolver) {
96
+ customResolver = resolver;
97
+ }
98
+ function resolveTheme(mode) {
99
+ if (customResolver) return customResolver(mode);
100
+ return mode === "dark" ? darkTheme : lightTheme;
101
+ }
102
+ var useThemeStore = zustand.create()(
103
+ middleware.persist(
104
+ (set, get) => ({
105
+ mode: systemScheme,
106
+ theme: resolveTheme(systemScheme),
107
+ _hasHydrated: false,
108
+ setHasHydrated: (state) => set({ _hasHydrated: state }),
109
+ toggleMode: () => {
110
+ const next = get().mode === "dark" ? "light" : "dark";
111
+ set({ mode: next, theme: resolveTheme(next) });
112
+ },
113
+ setMode: (mode) => {
114
+ set({ mode, theme: resolveTheme(mode) });
115
+ }
116
+ }),
117
+ {
118
+ name: "rn-theme-kit-storage",
119
+ storage: middleware.createJSONStorage(() => AsyncStorage__default.default),
120
+ partialize: (state) => ({ mode: state.mode }),
121
+ onRehydrateStorage: () => (state) => {
122
+ if (state) {
123
+ state.theme = resolveTheme(state.mode);
124
+ state.setHasHydrated(true);
125
+ }
126
+ }
127
+ }
128
+ )
129
+ );
130
+
131
+ // src/useTheme.ts
132
+ function useTheme() {
133
+ const theme = useThemeStore((s) => s.theme);
134
+ const mode = useThemeStore((s) => s.mode);
135
+ const toggleMode = useThemeStore((s) => s.toggleMode);
136
+ const setMode = useThemeStore((s) => s.setMode);
137
+ return { theme, mode, toggleMode, setMode, isDark: mode === "dark" };
138
+ }
139
+ function ThemeProvider({ children, fallback }) {
140
+ const hasHydrated = useThemeStore((s) => s._hasHydrated);
141
+ if (!hasHydrated) {
142
+ return fallback ? /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, fallback) : /* @__PURE__ */ React__default.default.createElement(reactNative.View, { style: styles.fallback }, /* @__PURE__ */ React__default.default.createElement(reactNative.ActivityIndicator, { size: "large" }));
143
+ }
144
+ return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, children);
145
+ }
146
+ var styles = reactNative.StyleSheet.create({
147
+ fallback: { flex: 1, justifyContent: "center", alignItems: "center" }
148
+ });
149
+
150
+ exports.ThemeProvider = ThemeProvider;
151
+ exports.baseTokens = baseTokens;
152
+ exports.darkTheme = darkTheme;
153
+ exports.lightTheme = lightTheme;
154
+ exports.registerThemeResolver = registerThemeResolver;
155
+ exports.useTheme = useTheme;
156
+ exports.useThemeStore = useThemeStore;
157
+ //# sourceMappingURL=index.js.map
158
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens.ts","../src/lightTheme.ts","../src/darkTheme.ts","../src/themeStore.ts","../src/useTheme.ts","../src/ThemeProvider.tsx"],"names":["Appearance","create","persist","createJSONStorage","AsyncStorage","React","View","ActivityIndicator","StyleSheet"],"mappings":"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","file":"index.js","sourcesContent":["export const baseTokens = {\r\n radius: {\r\n sm: 6,\r\n md: 12,\r\n lg: 20,\r\n xl: 24,\r\n },\r\n spacing: {\r\n xs: 4,\r\n sm: 8,\r\n md: 16,\r\n lg: 20,\r\n xl: 24,\r\n },\r\n fontSize: {\r\n sm: 12,\r\n md: 14,\r\n lg: 16,\r\n xl: 18,\r\n xxl: 22,\r\n xxxl: 26,\r\n xxxxl: 32,\r\n },\r\n fontWeight: {\r\n regular: '400' as const,\r\n medium: '600' as const,\r\n bold: '700' as const,\r\n },\r\n} as const;\r\n\r\nexport type BaseTokens = typeof baseTokens;","// lightTheme.ts\r\nimport { baseTokens } from './tokens';\r\n\r\nexport const lightTheme = {\r\n ...baseTokens,\r\n colors: {\r\n background: '#ffffff',\r\n surface: '#f8f8f8',\r\n card: '#ffffff',\r\n border: '#d1d1d1',\r\n inverted: '#000',\r\n invertedMuted: '#333333',\r\n invertedExtraMuted: '#ededed',\r\n boxShadow: 'rgba(0, 0, 0, 0.25)',\r\n textPrimary: '#000000',\r\n textSecondary: '#666666',\r\n textMuted: '#999999',\r\n accent: '#26702A',\r\n accentText: '#ffffff',\r\n success: '#2ecc71',\r\n warning: '#f39c12',\r\n info: '#3498db',\r\n danger: '#e74c3c',\r\n },\r\n} as const;\r\n\r\nexport type LightTheme = typeof lightTheme;","// darkTheme.ts\r\nimport { baseTokens } from './tokens';\r\n\r\nexport const darkTheme = {\r\n ...baseTokens,\r\n colors: {\r\n background: '#191919',\r\n surface: '#222222',\r\n card: '#1f1f1f',\r\n border: '#3a3a3a',\r\n inverted: '#fff',\r\n invertedMuted: '#333333',\r\n invertedExtraMuted: '#2e2e2e',\r\n boxShadow: 'rgba(255,255,255,0.13)',\r\n textPrimary: '#f5f5f5',\r\n textSecondary: '#b3b3b3',\r\n textMuted: '#9a9a9a',\r\n accent: '#26702A',\r\n accentText: '#ffffff',\r\n success: '#2ecc71',\r\n warning: '#f5b041',\r\n info: '#5dade2',\r\n danger: '#ec7063',\r\n },\r\n} as const;\r\n\r\nexport type DarkTheme = typeof darkTheme;","import { create } from 'zustand';\r\nimport { persist, createJSONStorage } from 'zustand/middleware';\r\nimport AsyncStorage from '@react-native-async-storage/async-storage';\r\nimport { Appearance } from 'react-native';\r\nimport { lightTheme, LightTheme } from './lightTheme';\r\nimport { darkTheme, DarkTheme } from './darkTheme';\r\n\r\nexport type ThemeMode = 'light' | 'dark';\r\nexport type Theme = LightTheme | DarkTheme;\r\n\r\nexport interface ThemeState {\r\n mode: ThemeMode;\r\n theme: Theme;\r\n _hasHydrated: boolean;\r\n setHasHydrated: (state: boolean) => void;\r\n toggleMode: () => void;\r\n setMode: (mode: ThemeMode) => void;\r\n}\r\n\r\nconst systemScheme = (Appearance.getColorScheme() ?? 'light') as ThemeMode;\r\n\r\n// Allow consumers to register custom theme resolvers\r\nlet customResolver: ((mode: ThemeMode) => Theme) | null = null;\r\n\r\nexport function registerThemeResolver(resolver: (mode: ThemeMode) => Theme) {\r\n customResolver = resolver;\r\n}\r\n\r\nfunction resolveTheme(mode: ThemeMode): Theme {\r\n if (customResolver) return customResolver(mode);\r\n return mode === 'dark' ? darkTheme : lightTheme;\r\n}\r\n\r\nexport const useThemeStore = create<ThemeState>()(\r\n persist(\r\n (set, get) => ({\r\n mode: systemScheme,\r\n theme: resolveTheme(systemScheme),\r\n _hasHydrated: false,\r\n\r\n setHasHydrated: (state) => set({ _hasHydrated: state }),\r\n\r\n toggleMode: () => {\r\n const next: ThemeMode = get().mode === 'dark' ? 'light' : 'dark';\r\n set({ mode: next, theme: resolveTheme(next) });\r\n },\r\n\r\n setMode: (mode: ThemeMode) => {\r\n set({ mode, theme: resolveTheme(mode) });\r\n },\r\n }),\r\n {\r\n name: 'rn-theme-kit-storage',\r\n storage: createJSONStorage(() => AsyncStorage),\r\n partialize: (state) => ({ mode: state.mode }),\r\n onRehydrateStorage: () => (state) => {\r\n if (state) {\r\n state.theme = resolveTheme(state.mode);\r\n state.setHasHydrated(true);\r\n }\r\n },\r\n }\r\n )\r\n);","import { useThemeStore } from './themeStore';\r\nimport type { Theme, ThemeMode } from './themeStore';\r\n\r\nexport function useTheme(): {\r\n theme: Theme;\r\n mode: ThemeMode;\r\n toggleMode: () => void;\r\n setMode: (mode: ThemeMode) => void;\r\n isDark: boolean;\r\n} {\r\n const theme = useThemeStore((s) => s.theme);\r\n const mode = useThemeStore((s) => s.mode);\r\n const toggleMode = useThemeStore((s) => s.toggleMode);\r\n const setMode = useThemeStore((s) => s.setMode);\r\n\r\n return { theme, mode, toggleMode, setMode, isDark: mode === 'dark' };\r\n}","import React, { ReactNode } from 'react';\r\nimport { View, ActivityIndicator, StyleSheet } from 'react-native';\r\nimport { useThemeStore } from './themeStore';\r\n\r\ninterface ThemeProviderProps {\r\n children: ReactNode;\r\n /** Custom loading fallback while AsyncStorage hydrates */\r\n fallback?: ReactNode;\r\n}\r\n\r\nexport function ThemeProvider({ children, fallback }: ThemeProviderProps) {\r\n const hasHydrated = useThemeStore((s) => s._hasHydrated);\r\n\r\n if (!hasHydrated) {\r\n return fallback ? (\r\n <>{fallback}</>\r\n ) : (\r\n <View style={styles.fallback}>\r\n <ActivityIndicator size=\"large\" />\r\n </View>\r\n );\r\n }\r\n\r\n return <>{children}</>;\r\n}\r\n\r\nconst styles = StyleSheet.create({\r\n fallback: { flex: 1, justifyContent: 'center', alignItems: 'center' },\r\n});"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,145 @@
1
+ import { create } from 'zustand';
2
+ import { persist, createJSONStorage } from 'zustand/middleware';
3
+ import AsyncStorage from '@react-native-async-storage/async-storage';
4
+ import { Appearance, StyleSheet, View, ActivityIndicator } from 'react-native';
5
+ import React from 'react';
6
+
7
+ // src/tokens.ts
8
+ var baseTokens = {
9
+ radius: {
10
+ sm: 6,
11
+ md: 12,
12
+ lg: 20,
13
+ xl: 24
14
+ },
15
+ spacing: {
16
+ xs: 4,
17
+ sm: 8,
18
+ md: 16,
19
+ lg: 20,
20
+ xl: 24
21
+ },
22
+ fontSize: {
23
+ sm: 12,
24
+ md: 14,
25
+ lg: 16,
26
+ xl: 18,
27
+ xxl: 22,
28
+ xxxl: 26,
29
+ xxxxl: 32
30
+ },
31
+ fontWeight: {
32
+ regular: "400",
33
+ medium: "600",
34
+ bold: "700"
35
+ }
36
+ };
37
+
38
+ // src/lightTheme.ts
39
+ var lightTheme = {
40
+ ...baseTokens,
41
+ colors: {
42
+ background: "#ffffff",
43
+ surface: "#f8f8f8",
44
+ card: "#ffffff",
45
+ border: "#d1d1d1",
46
+ inverted: "#000",
47
+ invertedMuted: "#333333",
48
+ invertedExtraMuted: "#ededed",
49
+ boxShadow: "rgba(0, 0, 0, 0.25)",
50
+ textPrimary: "#000000",
51
+ textSecondary: "#666666",
52
+ textMuted: "#999999",
53
+ accent: "#26702A",
54
+ accentText: "#ffffff",
55
+ success: "#2ecc71",
56
+ warning: "#f39c12",
57
+ info: "#3498db",
58
+ danger: "#e74c3c"
59
+ }
60
+ };
61
+
62
+ // src/darkTheme.ts
63
+ var darkTheme = {
64
+ ...baseTokens,
65
+ colors: {
66
+ background: "#191919",
67
+ surface: "#222222",
68
+ card: "#1f1f1f",
69
+ border: "#3a3a3a",
70
+ inverted: "#fff",
71
+ invertedMuted: "#333333",
72
+ invertedExtraMuted: "#2e2e2e",
73
+ boxShadow: "rgba(255,255,255,0.13)",
74
+ textPrimary: "#f5f5f5",
75
+ textSecondary: "#b3b3b3",
76
+ textMuted: "#9a9a9a",
77
+ accent: "#26702A",
78
+ accentText: "#ffffff",
79
+ success: "#2ecc71",
80
+ warning: "#f5b041",
81
+ info: "#5dade2",
82
+ danger: "#ec7063"
83
+ }
84
+ };
85
+ var _a;
86
+ var systemScheme = (_a = Appearance.getColorScheme()) != null ? _a : "light";
87
+ var customResolver = null;
88
+ function registerThemeResolver(resolver) {
89
+ customResolver = resolver;
90
+ }
91
+ function resolveTheme(mode) {
92
+ if (customResolver) return customResolver(mode);
93
+ return mode === "dark" ? darkTheme : lightTheme;
94
+ }
95
+ var useThemeStore = create()(
96
+ persist(
97
+ (set, get) => ({
98
+ mode: systemScheme,
99
+ theme: resolveTheme(systemScheme),
100
+ _hasHydrated: false,
101
+ setHasHydrated: (state) => set({ _hasHydrated: state }),
102
+ toggleMode: () => {
103
+ const next = get().mode === "dark" ? "light" : "dark";
104
+ set({ mode: next, theme: resolveTheme(next) });
105
+ },
106
+ setMode: (mode) => {
107
+ set({ mode, theme: resolveTheme(mode) });
108
+ }
109
+ }),
110
+ {
111
+ name: "rn-theme-kit-storage",
112
+ storage: createJSONStorage(() => AsyncStorage),
113
+ partialize: (state) => ({ mode: state.mode }),
114
+ onRehydrateStorage: () => (state) => {
115
+ if (state) {
116
+ state.theme = resolveTheme(state.mode);
117
+ state.setHasHydrated(true);
118
+ }
119
+ }
120
+ }
121
+ )
122
+ );
123
+
124
+ // src/useTheme.ts
125
+ function useTheme() {
126
+ const theme = useThemeStore((s) => s.theme);
127
+ const mode = useThemeStore((s) => s.mode);
128
+ const toggleMode = useThemeStore((s) => s.toggleMode);
129
+ const setMode = useThemeStore((s) => s.setMode);
130
+ return { theme, mode, toggleMode, setMode, isDark: mode === "dark" };
131
+ }
132
+ function ThemeProvider({ children, fallback }) {
133
+ const hasHydrated = useThemeStore((s) => s._hasHydrated);
134
+ if (!hasHydrated) {
135
+ return fallback ? /* @__PURE__ */ React.createElement(React.Fragment, null, fallback) : /* @__PURE__ */ React.createElement(View, { style: styles.fallback }, /* @__PURE__ */ React.createElement(ActivityIndicator, { size: "large" }));
136
+ }
137
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
138
+ }
139
+ var styles = StyleSheet.create({
140
+ fallback: { flex: 1, justifyContent: "center", alignItems: "center" }
141
+ });
142
+
143
+ export { ThemeProvider, baseTokens, darkTheme, lightTheme, registerThemeResolver, useTheme, useThemeStore };
144
+ //# sourceMappingURL=index.mjs.map
145
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens.ts","../src/lightTheme.ts","../src/darkTheme.ts","../src/themeStore.ts","../src/useTheme.ts","../src/ThemeProvider.tsx"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["export const baseTokens = {\r\n radius: {\r\n sm: 6,\r\n md: 12,\r\n lg: 20,\r\n xl: 24,\r\n },\r\n spacing: {\r\n xs: 4,\r\n sm: 8,\r\n md: 16,\r\n lg: 20,\r\n xl: 24,\r\n },\r\n fontSize: {\r\n sm: 12,\r\n md: 14,\r\n lg: 16,\r\n xl: 18,\r\n xxl: 22,\r\n xxxl: 26,\r\n xxxxl: 32,\r\n },\r\n fontWeight: {\r\n regular: '400' as const,\r\n medium: '600' as const,\r\n bold: '700' as const,\r\n },\r\n} as const;\r\n\r\nexport type BaseTokens = typeof baseTokens;","// lightTheme.ts\r\nimport { baseTokens } from './tokens';\r\n\r\nexport const lightTheme = {\r\n ...baseTokens,\r\n colors: {\r\n background: '#ffffff',\r\n surface: '#f8f8f8',\r\n card: '#ffffff',\r\n border: '#d1d1d1',\r\n inverted: '#000',\r\n invertedMuted: '#333333',\r\n invertedExtraMuted: '#ededed',\r\n boxShadow: 'rgba(0, 0, 0, 0.25)',\r\n textPrimary: '#000000',\r\n textSecondary: '#666666',\r\n textMuted: '#999999',\r\n accent: '#26702A',\r\n accentText: '#ffffff',\r\n success: '#2ecc71',\r\n warning: '#f39c12',\r\n info: '#3498db',\r\n danger: '#e74c3c',\r\n },\r\n} as const;\r\n\r\nexport type LightTheme = typeof lightTheme;","// darkTheme.ts\r\nimport { baseTokens } from './tokens';\r\n\r\nexport const darkTheme = {\r\n ...baseTokens,\r\n colors: {\r\n background: '#191919',\r\n surface: '#222222',\r\n card: '#1f1f1f',\r\n border: '#3a3a3a',\r\n inverted: '#fff',\r\n invertedMuted: '#333333',\r\n invertedExtraMuted: '#2e2e2e',\r\n boxShadow: 'rgba(255,255,255,0.13)',\r\n textPrimary: '#f5f5f5',\r\n textSecondary: '#b3b3b3',\r\n textMuted: '#9a9a9a',\r\n accent: '#26702A',\r\n accentText: '#ffffff',\r\n success: '#2ecc71',\r\n warning: '#f5b041',\r\n info: '#5dade2',\r\n danger: '#ec7063',\r\n },\r\n} as const;\r\n\r\nexport type DarkTheme = typeof darkTheme;","import { create } from 'zustand';\r\nimport { persist, createJSONStorage } from 'zustand/middleware';\r\nimport AsyncStorage from '@react-native-async-storage/async-storage';\r\nimport { Appearance } from 'react-native';\r\nimport { lightTheme, LightTheme } from './lightTheme';\r\nimport { darkTheme, DarkTheme } from './darkTheme';\r\n\r\nexport type ThemeMode = 'light' | 'dark';\r\nexport type Theme = LightTheme | DarkTheme;\r\n\r\nexport interface ThemeState {\r\n mode: ThemeMode;\r\n theme: Theme;\r\n _hasHydrated: boolean;\r\n setHasHydrated: (state: boolean) => void;\r\n toggleMode: () => void;\r\n setMode: (mode: ThemeMode) => void;\r\n}\r\n\r\nconst systemScheme = (Appearance.getColorScheme() ?? 'light') as ThemeMode;\r\n\r\n// Allow consumers to register custom theme resolvers\r\nlet customResolver: ((mode: ThemeMode) => Theme) | null = null;\r\n\r\nexport function registerThemeResolver(resolver: (mode: ThemeMode) => Theme) {\r\n customResolver = resolver;\r\n}\r\n\r\nfunction resolveTheme(mode: ThemeMode): Theme {\r\n if (customResolver) return customResolver(mode);\r\n return mode === 'dark' ? darkTheme : lightTheme;\r\n}\r\n\r\nexport const useThemeStore = create<ThemeState>()(\r\n persist(\r\n (set, get) => ({\r\n mode: systemScheme,\r\n theme: resolveTheme(systemScheme),\r\n _hasHydrated: false,\r\n\r\n setHasHydrated: (state) => set({ _hasHydrated: state }),\r\n\r\n toggleMode: () => {\r\n const next: ThemeMode = get().mode === 'dark' ? 'light' : 'dark';\r\n set({ mode: next, theme: resolveTheme(next) });\r\n },\r\n\r\n setMode: (mode: ThemeMode) => {\r\n set({ mode, theme: resolveTheme(mode) });\r\n },\r\n }),\r\n {\r\n name: 'rn-theme-kit-storage',\r\n storage: createJSONStorage(() => AsyncStorage),\r\n partialize: (state) => ({ mode: state.mode }),\r\n onRehydrateStorage: () => (state) => {\r\n if (state) {\r\n state.theme = resolveTheme(state.mode);\r\n state.setHasHydrated(true);\r\n }\r\n },\r\n }\r\n )\r\n);","import { useThemeStore } from './themeStore';\r\nimport type { Theme, ThemeMode } from './themeStore';\r\n\r\nexport function useTheme(): {\r\n theme: Theme;\r\n mode: ThemeMode;\r\n toggleMode: () => void;\r\n setMode: (mode: ThemeMode) => void;\r\n isDark: boolean;\r\n} {\r\n const theme = useThemeStore((s) => s.theme);\r\n const mode = useThemeStore((s) => s.mode);\r\n const toggleMode = useThemeStore((s) => s.toggleMode);\r\n const setMode = useThemeStore((s) => s.setMode);\r\n\r\n return { theme, mode, toggleMode, setMode, isDark: mode === 'dark' };\r\n}","import React, { ReactNode } from 'react';\r\nimport { View, ActivityIndicator, StyleSheet } from 'react-native';\r\nimport { useThemeStore } from './themeStore';\r\n\r\ninterface ThemeProviderProps {\r\n children: ReactNode;\r\n /** Custom loading fallback while AsyncStorage hydrates */\r\n fallback?: ReactNode;\r\n}\r\n\r\nexport function ThemeProvider({ children, fallback }: ThemeProviderProps) {\r\n const hasHydrated = useThemeStore((s) => s._hasHydrated);\r\n\r\n if (!hasHydrated) {\r\n return fallback ? (\r\n <>{fallback}</>\r\n ) : (\r\n <View style={styles.fallback}>\r\n <ActivityIndicator size=\"large\" />\r\n </View>\r\n );\r\n }\r\n\r\n return <>{children}</>;\r\n}\r\n\r\nconst styles = StyleSheet.create({\r\n fallback: { flex: 1, justifyContent: 'center', alignItems: 'center' },\r\n});"]}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "react-native-zustand-theme",
3
+ "version": "1.0.0",
4
+ "description": "A themeable design token library for React Native",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "build:watch": "tsup --watch",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "peerDependencies": {
24
+ "react": ">=18.0.0",
25
+ "react-native": ">=0.70.0",
26
+ "zustand": ">=4.0.0",
27
+ "@react-native-async-storage/async-storage": ">=1.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "tsup": "^8.0.0",
31
+ "typescript": "^5.0.0",
32
+ "@types/react": "^19.2.0",
33
+ "@types/react-native": "^0.72.0"
34
+ }
35
+ }