@react-easy-theme/core 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.
package/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # @react-easy-theme/core
2
+
3
+ A zero-configuration, lightweight, fully typed React theme management system. Built for modern web development, designed to "just work" with Next.js, Vite, Create React App, and Tailwind CSS.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@react-easy-theme/core.svg?style=flat-square)](https://www.npmjs.com/package/@react-easy-theme/core)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
8
+
9
+ ## Features ✨
10
+
11
+ * **Zero Dependencies**: Extremely lightweight (< 2kb minified + gzipped).
12
+ * **System Theme Detection**: Automatically respects user's OS preference (`prefers-color-scheme`).
13
+ * **Theme Persistence**: Remembers user choice via `localStorage`.
14
+ * **No Flash (FOUC)**: Optimized to prevent theme flashing on load (script injection supported).
15
+ * **Tailwind Compatible**: Works seamlessly with Tailwind's `dark` class strategy.
16
+ * **CSS Variables**: Fully customizable using native CSS variables for high performance.
17
+ * **Animated Transitions**: Smooth transitions between themes built-in.
18
+ * **Fully Typed**: Written in TypeScript with complete type definitions.
19
+
20
+ ---
21
+
22
+ ## Installation 📦
23
+
24
+ ```bash
25
+ npm install @react-easy-theme/core
26
+ # or
27
+ yarn add @react-easy-theme/core
28
+ # or
29
+ pnpm add @react-easy-theme/core
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Quick Start 🚀
35
+
36
+ ### 1. Wrap your application
37
+
38
+ Wrap your root component with `ThemeProvider`.
39
+
40
+ ```tsx
41
+ // src/App.tsx or src/main.tsx
42
+ import React from 'react';
43
+ import { ThemeProvider } from '@react-easy-theme/core';
44
+ import { Layout } from './Layout';
45
+
46
+ const App = () => {
47
+ return (
48
+ <ThemeProvider
49
+ defaultTheme="system"
50
+ themes={{
51
+ light: {
52
+ '--bg-primary': '#ffffff',
53
+ '--text-primary': '#1a202c',
54
+ },
55
+ dark: {
56
+ '--bg-primary': '#1a202c',
57
+ '--text-primary': '#ffffff',
58
+ },
59
+ }}
60
+ >
61
+ <Layout />
62
+ </ThemeProvider>
63
+ );
64
+ };
65
+
66
+ export default App;
67
+ ```
68
+
69
+ ### 2. Use the hook
70
+
71
+ Access the current theme and toggle it anywhere in your app using `useTheme`.
72
+
73
+ ```tsx
74
+ // src/components/Header.tsx
75
+ import React from 'react';
76
+ import { useTheme } from '@react-easy-theme/core';
77
+
78
+ const Header = () => {
79
+ const { theme, toggleTheme, resolvedTheme } = useTheme();
80
+
81
+ return (
82
+ <header style={{
83
+ backgroundColor: 'var(--bg-primary)',
84
+ color: 'var(--text-primary)'
85
+ }}>
86
+ <h1>My App</h1>
87
+ <button onClick={toggleTheme}>
88
+ Switch to {resolvedTheme === 'dark' ? 'Light' : 'Dark'}
89
+ </button>
90
+ </header>
91
+ );
92
+ };
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Tailwind CSS Integration 🌊
98
+
99
+ Reference: [Tailwind CSS Dark Mode](https://tailwindcss.com/docs/dark-mode)
100
+
101
+ 1. Enable "class" strategy in `tailwind.config.js`:
102
+
103
+ ```js
104
+ // tailwind.config.js
105
+ module.exports = {
106
+ darkMode: 'class', // Important!
107
+ // ...
108
+ }
109
+ ```
110
+
111
+ 2. Use standard Tailwind classes:
112
+
113
+ ```tsx
114
+ <div className="bg-white dark:bg-gray-900 text-black dark:text-white">
115
+ This component automatically updates based on the theme!
116
+ </div>
117
+ ```
118
+
119
+ `react-easy-theme` automatically adds/removes the `dark` class to the `<html>` element when the resolved theme is dark.
120
+
121
+ ---
122
+
123
+ ## Next.js Integration (SSR)
124
+
125
+ To prevent hydration mismatch warning in Next.js, follow this pattern (especially for App Router):
126
+
127
+ ```tsx
128
+ // app/layout.tsx
129
+ import { ThemeProvider } from '@react-easy-theme/core';
130
+
131
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
132
+ return (
133
+ <html lang="en">
134
+ <body>
135
+ <ThemeProvider>
136
+ {children}
137
+ </ThemeProvider>
138
+ </body>
139
+ </html>
140
+ );
141
+ }
142
+ ```
143
+
144
+ For "no flash of unstyled content" (FOUC) script injection support in older Pages Router, you can use our upcoming helper (roadmap item), or simply rely on `ThemeProvider`'s fast execution at the top of the tree. `react-easy-theme` handles `window` checks gracefully for SSR.
145
+
146
+ ---
147
+
148
+ ## API Documentation 📚
149
+
150
+ ### `<ThemeProvider />` Props
151
+
152
+ | Prop | Type | Default | Description |
153
+ | :--- | :--- | :--- | :--- |
154
+ | `defaultTheme` | `'light' \| 'dark' \| 'system'` | `'system'` | Initial theme if no storage found. |
155
+ | `themes` | `Record<string, ThemeVariables>` | `{}` | Object mapping theme names to CSS variables. |
156
+ | `storageKey` | `string` | `'theme-preference'` | Key used for `localStorage`. |
157
+ | `enableTransition` | `boolean` | `true` | Enables smooth CSS transition on change. |
158
+ | `syncWithTailwind` | `boolean` | `true` | Toggles `dark` class on `<html>`. |
159
+
160
+ ### `useTheme()` Return Values
161
+
162
+ | Value | Type | Description |
163
+ | :--- | :--- | :--- |
164
+ | `theme` | `'light' \| 'dark' \| 'system'` | The user's selected preference. |
165
+ | `resolvedTheme` | `'light' \| 'dark'` | The active theme (resolves 'system'). |
166
+ | `setTheme` | `(t: string) => void` | Updates standard theme preference. |
167
+ | `toggleTheme` | `() => void` | Toggles between light/dark. |
168
+ | `themes` | `Record` | The themes object passed to Provider. |
169
+
170
+ ---
171
+
172
+ ## Comparison 🆚
173
+
174
+ | Feature | React Easy Theme | Styled Components | Chakra UI |
175
+ | :--- | :--- | :--- | :--- |
176
+ | **Size** | **< 2kb** | ~12kb | ~100kb+ |
177
+ | **Approach** | CSS Variables + Native | CSS-in-JS Runtime | Component Library |
178
+ | **Build Setup** | Zero Config | Requires Babel plugin | Requires Setup |
179
+ | **SSR Support** | ✅ Built-in | ✅ Needs setup | ✅ Needs setup |
180
+ | **Tailwind** | ✅ First-class | ❌ Separate | ❌ Separate |
181
+
182
+ **Why choose React Easy Theme?**
183
+ If you want lightweight performance and prefer standard CSS/Tailwind over heavy JS runtime styling solutions, this is for you. It separates *theme logic* from *styling implementation*, giving you maximum flexibility.
184
+
185
+ ---
186
+
187
+ ## Roadmap 🗺️
188
+
189
+ 1. Add support for `custom` themes beyond just variable injection.
190
+ 2. Create a dedicated Next.js `<Script />` component for perfectly reliable anti-flicker.
191
+ 3. Add `useMedia` hook for responsive designs within JS.
192
+
193
+ ---
194
+
195
+ ## License
196
+
197
+ MIT © [Mehul](https://github.com/mehul)
@@ -0,0 +1,31 @@
1
+ import { ThemeContextProps, ThemeProviderProps, ThemeVariables } from './types/theme.types.mjs';
2
+ export { ThemeName } from './types/theme.types.mjs';
3
+ import * as React from 'react';
4
+ import React__default from 'react';
5
+
6
+ declare const ThemeContext: React.Context<ThemeContextProps | undefined>;
7
+
8
+ declare const useTheme: () => ThemeContextProps;
9
+
10
+ declare const ThemeProvider: React__default.FC<ThemeProviderProps>;
11
+
12
+ declare const ThemeSwitcher: React__default.FC;
13
+
14
+ declare const STORAGE_KEY = "theme-preference";
15
+ declare const getStorageTheme: (key?: string) => string | null;
16
+ declare const setStorageTheme: (key: string | undefined, theme: string) => void;
17
+
18
+ type SystemTheme = 'light' | 'dark';
19
+ declare const getSystemTheme: () => SystemTheme;
20
+ declare const subscribeToSystemTheme: (callback: (theme: SystemTheme) => void) => () => void;
21
+
22
+ declare const applyTheme: (theme: string, // 'light' | 'dark' | 'custom'
23
+ resolvedTheme: "light" | "dark", // Actual resolved visual theme based on system preference if 'system' is selected
24
+ themes: Record<string, ThemeVariables>, enableTransition?: boolean, syncWithTailwind?: boolean) => void;
25
+
26
+ declare const getThemeScript: (storageKey?: string, attribute?: string, defaultTheme?: string, value?: {
27
+ light: string;
28
+ dark: string;
29
+ }) => string;
30
+
31
+ export { STORAGE_KEY, type SystemTheme, ThemeContext, ThemeContextProps, ThemeProvider, ThemeProviderProps, ThemeSwitcher, ThemeVariables, applyTheme, getStorageTheme, getSystemTheme, getThemeScript, setStorageTheme, subscribeToSystemTheme, useTheme };
@@ -0,0 +1,31 @@
1
+ import { ThemeContextProps, ThemeProviderProps, ThemeVariables } from './types/theme.types.js';
2
+ export { ThemeName } from './types/theme.types.js';
3
+ import * as React from 'react';
4
+ import React__default from 'react';
5
+
6
+ declare const ThemeContext: React.Context<ThemeContextProps | undefined>;
7
+
8
+ declare const useTheme: () => ThemeContextProps;
9
+
10
+ declare const ThemeProvider: React__default.FC<ThemeProviderProps>;
11
+
12
+ declare const ThemeSwitcher: React__default.FC;
13
+
14
+ declare const STORAGE_KEY = "theme-preference";
15
+ declare const getStorageTheme: (key?: string) => string | null;
16
+ declare const setStorageTheme: (key: string | undefined, theme: string) => void;
17
+
18
+ type SystemTheme = 'light' | 'dark';
19
+ declare const getSystemTheme: () => SystemTheme;
20
+ declare const subscribeToSystemTheme: (callback: (theme: SystemTheme) => void) => () => void;
21
+
22
+ declare const applyTheme: (theme: string, // 'light' | 'dark' | 'custom'
23
+ resolvedTheme: "light" | "dark", // Actual resolved visual theme based on system preference if 'system' is selected
24
+ themes: Record<string, ThemeVariables>, enableTransition?: boolean, syncWithTailwind?: boolean) => void;
25
+
26
+ declare const getThemeScript: (storageKey?: string, attribute?: string, defaultTheme?: string, value?: {
27
+ light: string;
28
+ dark: string;
29
+ }) => string;
30
+
31
+ export { STORAGE_KEY, type SystemTheme, ThemeContext, ThemeContextProps, ThemeProvider, ThemeProviderProps, ThemeSwitcher, ThemeVariables, applyTheme, getStorageTheme, getSystemTheme, getThemeScript, setStorageTheme, subscribeToSystemTheme, useTheme };
package/dist/index.js ADDED
@@ -0,0 +1,27 @@
1
+ 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var c=react.createContext(void 0);var k=()=>{let s=react.useContext(c);if(!s)throw new Error("useTheme must be used within a ThemeProvider");return s};var x="theme-preference",v=(s=x)=>{if(typeof window>"u")return null;try{return window.localStorage.getItem(s)}catch(e){return console.warn("LocalStorage is not available:",e),null}},S=(s=x,e)=>{if(!(typeof window>"u"))try{window.localStorage.setItem(s,e);}catch(r){console.warn("LocalStorage is not available:",r);}};var l=()=>typeof window>"u"?"light":window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",b=s=>{if(typeof window>"u")return ()=>{};let e=window.matchMedia("(prefers-color-scheme: dark)"),r=n=>{s(n.matches?"dark":"light");};return e.addEventListener?(e.addEventListener("change",r),()=>e.removeEventListener("change",r)):(e.addListener(r),()=>e.removeListener(r))};var q=({children:s,defaultTheme:e="system",themes:r={},storageKey:n="theme-preference",enableTransition:i=true,syncWithTailwind:m=true})=>{let[o,d]=react.useState(()=>{if(typeof window<"u"){let t=v(n);if(t&&(t==="system"||r[t]||t==="light"||t==="dark"))return t}return e}),[a,u]=react.useState(()=>o==="system"?l():o==="light"||o==="dark"?o:"light"),[g,C]=react.useState(false);react.useEffect(()=>{C(true);},[]),react.useEffect(()=>{if(o==="system"){let t=()=>{u(l());},f=b(()=>{t();});return ()=>f()}},[o]),react.useEffect(()=>{u(o==="system"?l():o==="dark"?"dark":"light"),S(n,o);},[o,n]),react.useEffect(()=>{if(!g)return;let t=document.documentElement;m&&(a==="dark"?t.classList.add("dark"):t.classList.remove("dark"));let f=r[o==="system"?a:o]||r[a]||{};Object.entries(f).forEach(([P,L])=>{t.style.setProperty(P,L);}),t.style.colorScheme=a,i&&(t.style.transition="background-color 0.3s ease, color 0.3s ease");},[o,a,r,g,m,i]);let T=react.useCallback(t=>{d(t);},[]),y=react.useCallback(()=>{d(t=>t==="dark"?"light":"dark");},[]),E=react.useMemo(()=>({theme:o,resolvedTheme:a,setTheme:T,toggleTheme:y,themes:r}),[o,a,T,y,r]);return jsxRuntime.jsx(c.Provider,{value:E,children:s})};var U=()=>{let{theme:s,toggleTheme:e}=k();return jsxRuntime.jsx("button",{onClick:e,"aria-label":"Toggle theme",style:{padding:"0.5rem 1rem",borderRadius:"0.25rem",border:"1px solid currentColor",background:"transparent",cursor:"pointer",fontSize:"1rem",lineHeight:1},children:s==="dark"?"\u2600\uFE0F":"\u{1F319}"})};var W=(s,e,r,n=false,i=false)=>{if(typeof document>"u")return;let m=document.documentElement,o=r[e]||{};Object.entries(o).forEach(([d,a])=>{m.style.setProperty(d,a);}),n?m.style.setProperty("transition","background-color 0.3s ease, color 0.3s ease"):m.style.removeProperty("transition"),i&&(e==="dark"?m.classList.add("dark"):m.classList.remove("dark")),m.style.colorScheme=e;};var te=(s="theme-preference",e="class",r="system",n={light:"light",dark:"dark"})=>`(function() {
2
+ try {
3
+ var storageKey = '${s}';
4
+ var attribute = '${e}';
5
+ var defaultTheme = '${r}';
6
+ var darkVal = '${n.dark}';
7
+ var lightVal = '${n.light}';
8
+
9
+ var theme = localStorage.getItem(storageKey);
10
+ var support = window.matchMedia('(prefers-color-scheme: dark)').matches === true;
11
+
12
+ if (!theme && defaultTheme === 'system') {
13
+ theme = support ? 'dark' : 'light';
14
+ } else if (!theme) {
15
+ theme = defaultTheme;
16
+ }
17
+
18
+ if (theme === 'dark') {
19
+ document.documentElement.classList.add(darkVal);
20
+ document.documentElement.style.colorScheme = 'dark';
21
+ } else {
22
+ document.documentElement.classList.remove(darkVal);
23
+ document.documentElement.style.colorScheme = 'light';
24
+ }
25
+ } catch (e) {}
26
+ })();`;exports.STORAGE_KEY=x;exports.ThemeContext=c;exports.ThemeProvider=q;exports.ThemeSwitcher=U;exports.applyTheme=W;exports.getStorageTheme=v;exports.getSystemTheme=l;exports.getThemeScript=te;exports.setStorageTheme=S;exports.subscribeToSystemTheme=b;exports.useTheme=k;//# sourceMappingURL=index.js.map
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context/ThemeContext.tsx","../src/hooks/useTheme.ts","../src/core/storage.ts","../src/core/systemDetector.ts","../src/components/ThemeProvider.tsx","../src/components/ThemeSwitcher.tsx","../src/core/themeManager.ts","../src/core/script.ts"],"names":["ThemeContext","createContext","useTheme","context","useContext","STORAGE_KEY","getStorageTheme","key","setStorageTheme","theme","e","getSystemTheme","subscribeToSystemTheme","callback","mediaQuery","handleChange","ThemeProvider","children","defaultTheme","themes","storageKey","enableTransition","syncWithTailwind","setThemeState","useState","stored","resolvedTheme","setResolvedTheme","mounted","setMounted","useEffect","updateSystemTheme","unsubscribe","root","variables","value","setTheme","useCallback","newTheme","toggleTheme","prev","useMemo","jsx","ThemeSwitcher","applyTheme","themeVars","getThemeScript","attribute"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext } from 'react';\r\nimport { ThemeContextProps } from '../types/theme.types';\r\n\r\nexport const ThemeContext = createContext<ThemeContextProps | undefined>(undefined);\r\n","import { useContext } from 'react';\r\nimport { ThemeContext } from '../context/ThemeContext';\r\nimport { ThemeContextProps } from '../types/theme.types';\r\n\r\nexport const useTheme = (): ThemeContextProps => {\r\n const context = useContext(ThemeContext);\r\n if (!context) {\r\n throw new Error('useTheme must be used within a ThemeProvider');\r\n }\r\n return context;\r\n};\r\n","export const STORAGE_KEY = 'theme-preference';\r\n\r\nexport const getStorageTheme = (key: string = STORAGE_KEY): string | null => {\r\n if (typeof window === 'undefined') return null;\r\n try {\r\n return window.localStorage.getItem(key);\r\n } catch (e) {\r\n console.warn('LocalStorage is not available:', e);\r\n return null;\r\n }\r\n};\r\n\r\nexport const setStorageTheme = (key: string = STORAGE_KEY, theme: string): void => {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n window.localStorage.setItem(key, theme);\r\n } catch (e) {\r\n console.warn('LocalStorage is not available:', e);\r\n }\r\n};\r\n","export type SystemTheme = 'light' | 'dark';\r\n\r\nexport const getSystemTheme = (): SystemTheme => {\r\n if (typeof window === 'undefined') return 'light'; // Default for SSR\r\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\r\n};\r\n\r\nexport const subscribeToSystemTheme = (callback: (theme: SystemTheme) => void): () => void => {\r\n if (typeof window === 'undefined') return () => { };\r\n\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n\r\n const handleChange = (e: MediaQueryListEvent) => {\r\n callback(e.matches ? 'dark' : 'light');\r\n };\r\n\r\n // Modern browsers\r\n if (mediaQuery.addEventListener) {\r\n mediaQuery.addEventListener('change', handleChange);\r\n return () => mediaQuery.removeEventListener('change', handleChange);\r\n } else {\r\n // Fallback for older browsers (optional, but good for robust support)\r\n mediaQuery.addListener(handleChange);\r\n return () => mediaQuery.removeListener(handleChange);\r\n }\r\n};\r\n","import React, { useState, useEffect, useCallback, useMemo } from 'react';\r\nimport { ThemeContext } from '../context/ThemeContext';\r\nimport { ThemeName, ThemeVariables, ThemeProviderProps } from '../types/theme.types';\r\nimport { getStorageTheme, setStorageTheme } from '../core/storage';\r\nimport { getSystemTheme, subscribeToSystemTheme } from '../core/systemDetector';\r\n\r\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({\r\n children,\r\n defaultTheme = 'system',\r\n themes = {},\r\n storageKey = 'theme-preference',\r\n enableTransition = true,\r\n syncWithTailwind = true,\r\n}) => {\r\n // Initialize state from storage or default\r\n const [theme, setThemeState] = useState<ThemeName>(() => {\r\n if (typeof window !== 'undefined') {\r\n const stored = getStorageTheme(storageKey);\r\n if (stored && (stored === 'system' || themes[stored] || stored === 'light' || stored === 'dark')) {\r\n return stored;\r\n }\r\n }\r\n return defaultTheme;\r\n });\r\n\r\n const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>(() => {\r\n if (theme === 'system') return getSystemTheme();\r\n return (theme === 'light' || theme === 'dark') ? theme : 'light';\r\n });\r\n\r\n const [mounted, setMounted] = useState(false);\r\n\r\n // Hydration mismatch handling\r\n useEffect(() => {\r\n setMounted(true);\r\n }, []);\r\n\r\n // System theme listener\r\n useEffect(() => {\r\n if (theme === 'system') {\r\n const updateSystemTheme = () => {\r\n setResolvedTheme(getSystemTheme());\r\n };\r\n\r\n const unsubscribe = subscribeToSystemTheme(() => {\r\n updateSystemTheme(); // Update state on system change\r\n });\r\n\r\n return () => unsubscribe();\r\n }\r\n }, [theme]);\r\n\r\n // Update resolved theme when `theme` state changes\r\n useEffect(() => {\r\n if (theme === 'system') {\r\n setResolvedTheme(getSystemTheme());\r\n } else {\r\n // If it's a custom theme name, we might default to 'light' or 'dark' base if not specified\r\n // For simplicity, let's assume custom themes map to either light or dark base via configuration?\r\n // Or just default to 'light' for base resolved structure if unknown.\r\n // However, usually custom themes are just sets of variables.\r\n // Let's assume for resolvedTheme (which is used for conditional rendering logic often), \r\n // we check if the custom theme *name* implies dark/light or just default.\r\n // A common pattern is to have a `type` property in theme config, but here we just have variables.\r\n // Let's stick to: if it's not 'dark', it's 'light' unless we add more metadata.\r\n setResolvedTheme(theme === 'dark' ? 'dark' : 'light');\r\n }\r\n setStorageTheme(storageKey, theme);\r\n }, [theme, storageKey]);\r\n\r\n // Apply theme to DOM\r\n useEffect(() => {\r\n if (!mounted) return;\r\n\r\n const root = document.documentElement;\r\n\r\n // 1. Tailwind Class\r\n if (syncWithTailwind) {\r\n if (resolvedTheme === 'dark') {\r\n root.classList.add('dark');\r\n } else {\r\n root.classList.remove('dark');\r\n }\r\n }\r\n\r\n // 2. CSS Variables\r\n const variables = themes[theme === 'system' ? resolvedTheme : theme] || themes[resolvedTheme] || {};\r\n\r\n // Clear old vars? \r\n // Ideally we should but for performance let's just overwrite.\r\n // If a previous theme had a var that the new one doesn't, it might persist.\r\n // To fix this propery, we'd need to track injected keys. \r\n // For \"lightweight\", overwriting standard sets is usually enough.\r\n Object.entries(variables).forEach(([key, value]) => {\r\n root.style.setProperty(key, value);\r\n });\r\n\r\n // 3. Color Scheme\r\n root.style.colorScheme = resolvedTheme;\r\n\r\n // 4. Transitions\r\n if (enableTransition) {\r\n root.style.transition = 'background-color 0.3s ease, color 0.3s ease';\r\n // Cleanup transition just in case it interferes with later layout changes?\r\n // Let's leave it, standard practice for these libs.\r\n }\r\n\r\n }, [theme, resolvedTheme, themes, mounted, syncWithTailwind, enableTransition]);\r\n\r\n const setTheme = useCallback((newTheme: ThemeName) => {\r\n setThemeState(newTheme);\r\n }, []);\r\n\r\n const toggleTheme = useCallback(() => {\r\n setThemeState(prev => prev === 'dark' ? 'light' : 'dark');\r\n }, []);\r\n\r\n const value = useMemo(() => ({\r\n theme,\r\n resolvedTheme,\r\n setTheme,\r\n toggleTheme,\r\n themes\r\n }), [theme, resolvedTheme, setTheme, toggleTheme, themes]);\r\n\r\n // Render nothing or loading state until mounted to avoid mismatch? \r\n // Renders children immediately but with default/SSR state.\r\n // The script injection strategy is better for avoiding flash.\r\n return (\r\n <ThemeContext.Provider value={value}>\r\n {children}\r\n </ThemeContext.Provider>\r\n );\r\n};\r\n","import React from 'react';\r\nimport { useTheme } from '../hooks/useTheme';\r\n\r\nexport const ThemeSwitcher: React.FC = () => {\r\n const { theme, toggleTheme } = useTheme();\r\n\r\n return (\r\n <button\r\n onClick={toggleTheme}\r\n aria-label=\"Toggle theme\"\r\n style={{\r\n padding: '0.5rem 1rem',\r\n borderRadius: '0.25rem',\r\n border: '1px solid currentColor',\r\n background: 'transparent',\r\n cursor: 'pointer',\r\n fontSize: '1rem',\r\n lineHeight: 1,\r\n }}\r\n >\r\n {theme === 'dark' ? '☀️' : '🌙'}\r\n </button>\r\n );\r\n};\r\n","import { ThemeVariables } from '../types/theme.types';\r\n\r\nexport const applyTheme = (\r\n theme: string, // 'light' | 'dark' | 'custom'\r\n resolvedTheme: 'light' | 'dark', // Actual resolved visual theme based on system preference if 'system' is selected\r\n themes: Record<string, ThemeVariables>,\r\n enableTransition: boolean = false,\r\n syncWithTailwind: boolean = false\r\n) => {\r\n if (typeof document === 'undefined') return;\r\n\r\n const root = document.documentElement;\r\n\r\n // Apply CSS Variables\r\n const themeVars = themes[resolvedTheme] || {};\r\n Object.entries(themeVars).forEach(([key, value]) => {\r\n root.style.setProperty(key, value);\r\n });\r\n\r\n // Handle Transitions\r\n if (enableTransition) {\r\n root.style.setProperty('transition', 'background-color 0.3s ease, color 0.3s ease');\r\n // Remove transition after it completes to avoid performance hit on other changes? \r\n // Usually keeping it is fine if scoped to specific properties.\r\n } else {\r\n root.style.removeProperty('transition');\r\n }\r\n\r\n // Tailwind Dark Mode Class Strategy\r\n if (syncWithTailwind) {\r\n if (resolvedTheme === 'dark') {\r\n root.classList.add('dark');\r\n } else {\r\n root.classList.remove('dark');\r\n }\r\n }\r\n\r\n // Set color-scheme for native browser UI (scrollbars, etc.)\r\n root.style.colorScheme = resolvedTheme;\r\n};\r\n","export const getThemeScript = (\r\n storageKey: string = 'theme-preference',\r\n attribute: string = 'class',\r\n defaultTheme: string = 'system',\r\n value: { light: string; dark: string } = { light: 'light', dark: 'dark' },\r\n) => {\r\n return `(function() {\r\n try {\r\n var storageKey = '${storageKey}';\r\n var attribute = '${attribute}';\r\n var defaultTheme = '${defaultTheme}';\r\n var darkVal = '${value.dark}';\r\n var lightVal = '${value.light}';\r\n\r\n var theme = localStorage.getItem(storageKey);\r\n var support = window.matchMedia('(prefers-color-scheme: dark)').matches === true;\r\n\r\n if (!theme && defaultTheme === 'system') {\r\n theme = support ? 'dark' : 'light';\r\n } else if (!theme) {\r\n theme = defaultTheme;\r\n }\r\n\r\n if (theme === 'dark') {\r\n document.documentElement.classList.add(darkVal);\r\n document.documentElement.style.colorScheme = 'dark';\r\n } else {\r\n document.documentElement.classList.remove(darkVal);\r\n document.documentElement.style.colorScheme = 'light';\r\n }\r\n } catch (e) {}\r\n })();`;\r\n};\r\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,27 @@
1
+ import {createContext,useContext,useState,useEffect,useCallback,useMemo}from'react';import {jsx}from'react/jsx-runtime';var c=createContext(void 0);var k=()=>{let s=useContext(c);if(!s)throw new Error("useTheme must be used within a ThemeProvider");return s};var x="theme-preference",v=(s=x)=>{if(typeof window>"u")return null;try{return window.localStorage.getItem(s)}catch(e){return console.warn("LocalStorage is not available:",e),null}},S=(s=x,e)=>{if(!(typeof window>"u"))try{window.localStorage.setItem(s,e);}catch(r){console.warn("LocalStorage is not available:",r);}};var l=()=>typeof window>"u"?"light":window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light",b=s=>{if(typeof window>"u")return ()=>{};let e=window.matchMedia("(prefers-color-scheme: dark)"),r=n=>{s(n.matches?"dark":"light");};return e.addEventListener?(e.addEventListener("change",r),()=>e.removeEventListener("change",r)):(e.addListener(r),()=>e.removeListener(r))};var q=({children:s,defaultTheme:e="system",themes:r={},storageKey:n="theme-preference",enableTransition:i=true,syncWithTailwind:m=true})=>{let[o,d]=useState(()=>{if(typeof window<"u"){let t=v(n);if(t&&(t==="system"||r[t]||t==="light"||t==="dark"))return t}return e}),[a,u]=useState(()=>o==="system"?l():o==="light"||o==="dark"?o:"light"),[g,C]=useState(false);useEffect(()=>{C(true);},[]),useEffect(()=>{if(o==="system"){let t=()=>{u(l());},f=b(()=>{t();});return ()=>f()}},[o]),useEffect(()=>{u(o==="system"?l():o==="dark"?"dark":"light"),S(n,o);},[o,n]),useEffect(()=>{if(!g)return;let t=document.documentElement;m&&(a==="dark"?t.classList.add("dark"):t.classList.remove("dark"));let f=r[o==="system"?a:o]||r[a]||{};Object.entries(f).forEach(([P,L])=>{t.style.setProperty(P,L);}),t.style.colorScheme=a,i&&(t.style.transition="background-color 0.3s ease, color 0.3s ease");},[o,a,r,g,m,i]);let T=useCallback(t=>{d(t);},[]),y=useCallback(()=>{d(t=>t==="dark"?"light":"dark");},[]),E=useMemo(()=>({theme:o,resolvedTheme:a,setTheme:T,toggleTheme:y,themes:r}),[o,a,T,y,r]);return jsx(c.Provider,{value:E,children:s})};var U=()=>{let{theme:s,toggleTheme:e}=k();return jsx("button",{onClick:e,"aria-label":"Toggle theme",style:{padding:"0.5rem 1rem",borderRadius:"0.25rem",border:"1px solid currentColor",background:"transparent",cursor:"pointer",fontSize:"1rem",lineHeight:1},children:s==="dark"?"\u2600\uFE0F":"\u{1F319}"})};var W=(s,e,r,n=false,i=false)=>{if(typeof document>"u")return;let m=document.documentElement,o=r[e]||{};Object.entries(o).forEach(([d,a])=>{m.style.setProperty(d,a);}),n?m.style.setProperty("transition","background-color 0.3s ease, color 0.3s ease"):m.style.removeProperty("transition"),i&&(e==="dark"?m.classList.add("dark"):m.classList.remove("dark")),m.style.colorScheme=e;};var te=(s="theme-preference",e="class",r="system",n={light:"light",dark:"dark"})=>`(function() {
2
+ try {
3
+ var storageKey = '${s}';
4
+ var attribute = '${e}';
5
+ var defaultTheme = '${r}';
6
+ var darkVal = '${n.dark}';
7
+ var lightVal = '${n.light}';
8
+
9
+ var theme = localStorage.getItem(storageKey);
10
+ var support = window.matchMedia('(prefers-color-scheme: dark)').matches === true;
11
+
12
+ if (!theme && defaultTheme === 'system') {
13
+ theme = support ? 'dark' : 'light';
14
+ } else if (!theme) {
15
+ theme = defaultTheme;
16
+ }
17
+
18
+ if (theme === 'dark') {
19
+ document.documentElement.classList.add(darkVal);
20
+ document.documentElement.style.colorScheme = 'dark';
21
+ } else {
22
+ document.documentElement.classList.remove(darkVal);
23
+ document.documentElement.style.colorScheme = 'light';
24
+ }
25
+ } catch (e) {}
26
+ })();`;export{x as STORAGE_KEY,c as ThemeContext,q as ThemeProvider,U as ThemeSwitcher,W as applyTheme,v as getStorageTheme,l as getSystemTheme,te as getThemeScript,S as setStorageTheme,b as subscribeToSystemTheme,k as useTheme};//# sourceMappingURL=index.mjs.map
27
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/context/ThemeContext.tsx","../src/hooks/useTheme.ts","../src/core/storage.ts","../src/core/systemDetector.ts","../src/components/ThemeProvider.tsx","../src/components/ThemeSwitcher.tsx","../src/core/themeManager.ts","../src/core/script.ts"],"names":["ThemeContext","createContext","useTheme","context","useContext","STORAGE_KEY","getStorageTheme","key","setStorageTheme","theme","e","getSystemTheme","subscribeToSystemTheme","callback","mediaQuery","handleChange","ThemeProvider","children","defaultTheme","themes","storageKey","enableTransition","syncWithTailwind","setThemeState","useState","stored","resolvedTheme","setResolvedTheme","mounted","setMounted","useEffect","updateSystemTheme","unsubscribe","root","variables","value","setTheme","useCallback","newTheme","toggleTheme","prev","useMemo","jsx","ThemeSwitcher","applyTheme","themeVars","getThemeScript","attribute"],"mappings":"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","file":"index.mjs","sourcesContent":["import { createContext } from 'react';\r\nimport { ThemeContextProps } from '../types/theme.types';\r\n\r\nexport const ThemeContext = createContext<ThemeContextProps | undefined>(undefined);\r\n","import { useContext } from 'react';\r\nimport { ThemeContext } from '../context/ThemeContext';\r\nimport { ThemeContextProps } from '../types/theme.types';\r\n\r\nexport const useTheme = (): ThemeContextProps => {\r\n const context = useContext(ThemeContext);\r\n if (!context) {\r\n throw new Error('useTheme must be used within a ThemeProvider');\r\n }\r\n return context;\r\n};\r\n","export const STORAGE_KEY = 'theme-preference';\r\n\r\nexport const getStorageTheme = (key: string = STORAGE_KEY): string | null => {\r\n if (typeof window === 'undefined') return null;\r\n try {\r\n return window.localStorage.getItem(key);\r\n } catch (e) {\r\n console.warn('LocalStorage is not available:', e);\r\n return null;\r\n }\r\n};\r\n\r\nexport const setStorageTheme = (key: string = STORAGE_KEY, theme: string): void => {\r\n if (typeof window === 'undefined') return;\r\n try {\r\n window.localStorage.setItem(key, theme);\r\n } catch (e) {\r\n console.warn('LocalStorage is not available:', e);\r\n }\r\n};\r\n","export type SystemTheme = 'light' | 'dark';\r\n\r\nexport const getSystemTheme = (): SystemTheme => {\r\n if (typeof window === 'undefined') return 'light'; // Default for SSR\r\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\r\n};\r\n\r\nexport const subscribeToSystemTheme = (callback: (theme: SystemTheme) => void): () => void => {\r\n if (typeof window === 'undefined') return () => { };\r\n\r\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\r\n\r\n const handleChange = (e: MediaQueryListEvent) => {\r\n callback(e.matches ? 'dark' : 'light');\r\n };\r\n\r\n // Modern browsers\r\n if (mediaQuery.addEventListener) {\r\n mediaQuery.addEventListener('change', handleChange);\r\n return () => mediaQuery.removeEventListener('change', handleChange);\r\n } else {\r\n // Fallback for older browsers (optional, but good for robust support)\r\n mediaQuery.addListener(handleChange);\r\n return () => mediaQuery.removeListener(handleChange);\r\n }\r\n};\r\n","import React, { useState, useEffect, useCallback, useMemo } from 'react';\r\nimport { ThemeContext } from '../context/ThemeContext';\r\nimport { ThemeName, ThemeVariables, ThemeProviderProps } from '../types/theme.types';\r\nimport { getStorageTheme, setStorageTheme } from '../core/storage';\r\nimport { getSystemTheme, subscribeToSystemTheme } from '../core/systemDetector';\r\n\r\nexport const ThemeProvider: React.FC<ThemeProviderProps> = ({\r\n children,\r\n defaultTheme = 'system',\r\n themes = {},\r\n storageKey = 'theme-preference',\r\n enableTransition = true,\r\n syncWithTailwind = true,\r\n}) => {\r\n // Initialize state from storage or default\r\n const [theme, setThemeState] = useState<ThemeName>(() => {\r\n if (typeof window !== 'undefined') {\r\n const stored = getStorageTheme(storageKey);\r\n if (stored && (stored === 'system' || themes[stored] || stored === 'light' || stored === 'dark')) {\r\n return stored;\r\n }\r\n }\r\n return defaultTheme;\r\n });\r\n\r\n const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>(() => {\r\n if (theme === 'system') return getSystemTheme();\r\n return (theme === 'light' || theme === 'dark') ? theme : 'light';\r\n });\r\n\r\n const [mounted, setMounted] = useState(false);\r\n\r\n // Hydration mismatch handling\r\n useEffect(() => {\r\n setMounted(true);\r\n }, []);\r\n\r\n // System theme listener\r\n useEffect(() => {\r\n if (theme === 'system') {\r\n const updateSystemTheme = () => {\r\n setResolvedTheme(getSystemTheme());\r\n };\r\n\r\n const unsubscribe = subscribeToSystemTheme(() => {\r\n updateSystemTheme(); // Update state on system change\r\n });\r\n\r\n return () => unsubscribe();\r\n }\r\n }, [theme]);\r\n\r\n // Update resolved theme when `theme` state changes\r\n useEffect(() => {\r\n if (theme === 'system') {\r\n setResolvedTheme(getSystemTheme());\r\n } else {\r\n // If it's a custom theme name, we might default to 'light' or 'dark' base if not specified\r\n // For simplicity, let's assume custom themes map to either light or dark base via configuration?\r\n // Or just default to 'light' for base resolved structure if unknown.\r\n // However, usually custom themes are just sets of variables.\r\n // Let's assume for resolvedTheme (which is used for conditional rendering logic often), \r\n // we check if the custom theme *name* implies dark/light or just default.\r\n // A common pattern is to have a `type` property in theme config, but here we just have variables.\r\n // Let's stick to: if it's not 'dark', it's 'light' unless we add more metadata.\r\n setResolvedTheme(theme === 'dark' ? 'dark' : 'light');\r\n }\r\n setStorageTheme(storageKey, theme);\r\n }, [theme, storageKey]);\r\n\r\n // Apply theme to DOM\r\n useEffect(() => {\r\n if (!mounted) return;\r\n\r\n const root = document.documentElement;\r\n\r\n // 1. Tailwind Class\r\n if (syncWithTailwind) {\r\n if (resolvedTheme === 'dark') {\r\n root.classList.add('dark');\r\n } else {\r\n root.classList.remove('dark');\r\n }\r\n }\r\n\r\n // 2. CSS Variables\r\n const variables = themes[theme === 'system' ? resolvedTheme : theme] || themes[resolvedTheme] || {};\r\n\r\n // Clear old vars? \r\n // Ideally we should but for performance let's just overwrite.\r\n // If a previous theme had a var that the new one doesn't, it might persist.\r\n // To fix this propery, we'd need to track injected keys. \r\n // For \"lightweight\", overwriting standard sets is usually enough.\r\n Object.entries(variables).forEach(([key, value]) => {\r\n root.style.setProperty(key, value);\r\n });\r\n\r\n // 3. Color Scheme\r\n root.style.colorScheme = resolvedTheme;\r\n\r\n // 4. Transitions\r\n if (enableTransition) {\r\n root.style.transition = 'background-color 0.3s ease, color 0.3s ease';\r\n // Cleanup transition just in case it interferes with later layout changes?\r\n // Let's leave it, standard practice for these libs.\r\n }\r\n\r\n }, [theme, resolvedTheme, themes, mounted, syncWithTailwind, enableTransition]);\r\n\r\n const setTheme = useCallback((newTheme: ThemeName) => {\r\n setThemeState(newTheme);\r\n }, []);\r\n\r\n const toggleTheme = useCallback(() => {\r\n setThemeState(prev => prev === 'dark' ? 'light' : 'dark');\r\n }, []);\r\n\r\n const value = useMemo(() => ({\r\n theme,\r\n resolvedTheme,\r\n setTheme,\r\n toggleTheme,\r\n themes\r\n }), [theme, resolvedTheme, setTheme, toggleTheme, themes]);\r\n\r\n // Render nothing or loading state until mounted to avoid mismatch? \r\n // Renders children immediately but with default/SSR state.\r\n // The script injection strategy is better for avoiding flash.\r\n return (\r\n <ThemeContext.Provider value={value}>\r\n {children}\r\n </ThemeContext.Provider>\r\n );\r\n};\r\n","import React from 'react';\r\nimport { useTheme } from '../hooks/useTheme';\r\n\r\nexport const ThemeSwitcher: React.FC = () => {\r\n const { theme, toggleTheme } = useTheme();\r\n\r\n return (\r\n <button\r\n onClick={toggleTheme}\r\n aria-label=\"Toggle theme\"\r\n style={{\r\n padding: '0.5rem 1rem',\r\n borderRadius: '0.25rem',\r\n border: '1px solid currentColor',\r\n background: 'transparent',\r\n cursor: 'pointer',\r\n fontSize: '1rem',\r\n lineHeight: 1,\r\n }}\r\n >\r\n {theme === 'dark' ? '☀️' : '🌙'}\r\n </button>\r\n );\r\n};\r\n","import { ThemeVariables } from '../types/theme.types';\r\n\r\nexport const applyTheme = (\r\n theme: string, // 'light' | 'dark' | 'custom'\r\n resolvedTheme: 'light' | 'dark', // Actual resolved visual theme based on system preference if 'system' is selected\r\n themes: Record<string, ThemeVariables>,\r\n enableTransition: boolean = false,\r\n syncWithTailwind: boolean = false\r\n) => {\r\n if (typeof document === 'undefined') return;\r\n\r\n const root = document.documentElement;\r\n\r\n // Apply CSS Variables\r\n const themeVars = themes[resolvedTheme] || {};\r\n Object.entries(themeVars).forEach(([key, value]) => {\r\n root.style.setProperty(key, value);\r\n });\r\n\r\n // Handle Transitions\r\n if (enableTransition) {\r\n root.style.setProperty('transition', 'background-color 0.3s ease, color 0.3s ease');\r\n // Remove transition after it completes to avoid performance hit on other changes? \r\n // Usually keeping it is fine if scoped to specific properties.\r\n } else {\r\n root.style.removeProperty('transition');\r\n }\r\n\r\n // Tailwind Dark Mode Class Strategy\r\n if (syncWithTailwind) {\r\n if (resolvedTheme === 'dark') {\r\n root.classList.add('dark');\r\n } else {\r\n root.classList.remove('dark');\r\n }\r\n }\r\n\r\n // Set color-scheme for native browser UI (scrollbars, etc.)\r\n root.style.colorScheme = resolvedTheme;\r\n};\r\n","export const getThemeScript = (\r\n storageKey: string = 'theme-preference',\r\n attribute: string = 'class',\r\n defaultTheme: string = 'system',\r\n value: { light: string; dark: string } = { light: 'light', dark: 'dark' },\r\n) => {\r\n return `(function() {\r\n try {\r\n var storageKey = '${storageKey}';\r\n var attribute = '${attribute}';\r\n var defaultTheme = '${defaultTheme}';\r\n var darkVal = '${value.dark}';\r\n var lightVal = '${value.light}';\r\n\r\n var theme = localStorage.getItem(storageKey);\r\n var support = window.matchMedia('(prefers-color-scheme: dark)').matches === true;\r\n\r\n if (!theme && defaultTheme === 'system') {\r\n theme = support ? 'dark' : 'light';\r\n } else if (!theme) {\r\n theme = defaultTheme;\r\n }\r\n\r\n if (theme === 'dark') {\r\n document.documentElement.classList.add(darkVal);\r\n document.documentElement.style.colorScheme = 'dark';\r\n } else {\r\n document.documentElement.classList.remove(darkVal);\r\n document.documentElement.style.colorScheme = 'light';\r\n }\r\n } catch (e) {}\r\n })();`;\r\n};\r\n"]}
@@ -0,0 +1,24 @@
1
+ type ThemeVariables = Record<string, string>;
2
+ type ThemeName = 'light' | 'dark' | 'system' | string;
3
+ interface ThemeContextProps {
4
+ theme: ThemeName;
5
+ resolvedTheme: 'light' | 'dark';
6
+ setTheme: (theme: ThemeName) => void;
7
+ toggleTheme: () => void;
8
+ themes: Record<string, ThemeVariables>;
9
+ }
10
+ interface ThemeProviderProps {
11
+ children: React.ReactNode;
12
+ defaultTheme?: ThemeName;
13
+ themes?: Record<string, ThemeVariables>;
14
+ storageKey?: string;
15
+ enableTransition?: boolean;
16
+ syncWithTailwind?: boolean;
17
+ /**
18
+ * Optional initial theme for SSR to avoid hydration mismatch if known.
19
+ * Usually not needed if script injection is used.
20
+ */
21
+ forcedTheme?: ThemeName;
22
+ }
23
+
24
+ export type { ThemeContextProps, ThemeName, ThemeProviderProps, ThemeVariables };
@@ -0,0 +1,24 @@
1
+ type ThemeVariables = Record<string, string>;
2
+ type ThemeName = 'light' | 'dark' | 'system' | string;
3
+ interface ThemeContextProps {
4
+ theme: ThemeName;
5
+ resolvedTheme: 'light' | 'dark';
6
+ setTheme: (theme: ThemeName) => void;
7
+ toggleTheme: () => void;
8
+ themes: Record<string, ThemeVariables>;
9
+ }
10
+ interface ThemeProviderProps {
11
+ children: React.ReactNode;
12
+ defaultTheme?: ThemeName;
13
+ themes?: Record<string, ThemeVariables>;
14
+ storageKey?: string;
15
+ enableTransition?: boolean;
16
+ syncWithTailwind?: boolean;
17
+ /**
18
+ * Optional initial theme for SSR to avoid hydration mismatch if known.
19
+ * Usually not needed if script injection is used.
20
+ */
21
+ forcedTheme?: ThemeName;
22
+ }
23
+
24
+ export type { ThemeContextProps, ThemeName, ThemeProviderProps, ThemeVariables };
@@ -0,0 +1,2 @@
1
+ 'use strict';//# sourceMappingURL=theme.types.js.map
2
+ //# sourceMappingURL=theme.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"theme.types.js"}
@@ -0,0 +1,2 @@
1
+ //# sourceMappingURL=theme.types.mjs.map
2
+ //# sourceMappingURL=theme.types.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"theme.types.mjs"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@react-easy-theme/core",
3
+ "version": "1.0.0",
4
+ "description": "Zero-configuration, lightweight, fully typed React theme management system.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsup",
10
+ "dev": "tsup --watch",
11
+ "lint": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "peerDependencies": {
15
+ "react": ">=16.8.0",
16
+ "react-dom": ">=16.8.0"
17
+ },
18
+ "devDependencies": {
19
+ "react": "^18.2.0",
20
+ "react-dom": "^18.2.0",
21
+ "typescript": "^5.0.0",
22
+ "tsup": "^8.0.0",
23
+ "@types/react": "^18.2.0",
24
+ "@types/react-dom": "^18.2.0"
25
+ },
26
+ "keywords": [
27
+ "react",
28
+ "theme",
29
+ "dark-mode",
30
+ "light-mode",
31
+ "css-variables",
32
+ "tailwind",
33
+ "hooks",
34
+ "provider"
35
+ ],
36
+ "author": "Mehulbirare",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/Mehulbirare/react-easy-theme.git"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/Mehulbirare/react-easy-theme/issues"
44
+ },
45
+ "homepage": "https://github.com/Mehulbirare/react-easy-theme#readme",
46
+ "files": [
47
+ "dist",
48
+ "README.md"
49
+ ]
50
+ }