@space-man/react-theme-animation 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/LICENSE +21 -0
- package/README.md +623 -0
- package/dist/cjs/components/SpacemanThemeProvider.d.ts +51 -0
- package/dist/cjs/components/SpacemanThemeProvider.js +93 -0
- package/dist/cjs/components/ThemeSelector.d.ts +3 -0
- package/dist/cjs/components/ThemeSelector.js +39 -0
- package/dist/cjs/components/ThemeSwitcher.d.ts +3 -0
- package/dist/cjs/components/ThemeSwitcher.js +100 -0
- package/dist/cjs/components/ui/select.d.ts +15 -0
- package/dist/cjs/components/ui/select.js +83 -0
- package/dist/cjs/hooks/use-theme-animation.d.ts +2 -0
- package/dist/cjs/hooks/use-theme-animation.js +149 -0
- package/dist/cjs/index.d.ts +7 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/types/index.d.ts +65 -0
- package/dist/cjs/types/index.js +8 -0
- package/dist/cjs/utils/animations.d.ts +18 -0
- package/dist/cjs/utils/animations.js +156 -0
- package/dist/components/SpacemanThemeProvider.d.ts +51 -0
- package/dist/components/SpacemanThemeProvider.js +88 -0
- package/dist/components/ThemeSelector.d.ts +3 -0
- package/dist/components/ThemeSelector.js +35 -0
- package/dist/components/ThemeSwitcher.d.ts +3 -0
- package/dist/components/ThemeSwitcher.js +96 -0
- package/dist/components/ui/select.d.ts +15 -0
- package/dist/components/ui/select.js +39 -0
- package/dist/esm/components/SpacemanThemeProvider.d.ts +51 -0
- package/dist/esm/components/SpacemanThemeProvider.js +88 -0
- package/dist/esm/components/ThemeSelector.d.ts +3 -0
- package/dist/esm/components/ThemeSelector.js +35 -0
- package/dist/esm/components/ThemeSwitcher.d.ts +3 -0
- package/dist/esm/components/ThemeSwitcher.js +96 -0
- package/dist/esm/components/ui/select.d.ts +15 -0
- package/dist/esm/components/ui/select.js +39 -0
- package/dist/esm/hooks/use-theme-animation.d.ts +2 -0
- package/dist/esm/hooks/use-theme-animation.js +145 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/types/index.d.ts +65 -0
- package/dist/esm/types/index.js +5 -0
- package/dist/esm/utils/animations.d.ts +18 -0
- package/dist/esm/utils/animations.js +145 -0
- package/dist/hooks/use-theme-animation.d.ts +2 -0
- package/dist/hooks/use-theme-animation.js +145 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/types/index.d.ts +65 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/animations.d.ts +18 -0
- package/dist/utils/animations.js +145 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tony Edgal
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
# @spaceman/react-theme-animation
|
|
2
|
+
|
|
3
|
+
React theme switching with smooth view transition animations, multi-theme support, and synchronized state management.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **Smooth Animations**: Beautiful view transition animations for theme switching with customizable origins
|
|
8
|
+
- 🌓 **Multi-Theme Support**: Support for light, dark, and system themes
|
|
9
|
+
- 🎯 **Color Themes**: Additional color theme variants (brand colors, etc.)
|
|
10
|
+
- 🪝 **Powerful Hook**: `useThemeAnimation` hook with full control
|
|
11
|
+
- 🧩 **Ready Components**: `ThemeSwitcher` and `ThemeSelector` components
|
|
12
|
+
- 🎛️ **Provider Pattern**: Centralized theme state management with `SpacemanThemeProvider`
|
|
13
|
+
- 🔄 **State Synchronization**: Prevents state drift between multiple components
|
|
14
|
+
- 📱 **Responsive**: Works on all screen sizes including high-resolution displays
|
|
15
|
+
- ⚡ **Performance**: Optimized animations with reduced motion support
|
|
16
|
+
- 🔧 **TypeScript**: Full TypeScript support with comprehensive types
|
|
17
|
+
- 🎛️ **Customizable**: Extensive configuration options
|
|
18
|
+
- 🔙 **Backward Compatible**: Works with existing implementations
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @spaceman/react-theme-animation
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage Patterns
|
|
27
|
+
|
|
28
|
+
This library supports two main usage patterns:
|
|
29
|
+
|
|
30
|
+
1. **Hook-Only Usage**: Direct hook usage for custom implementations
|
|
31
|
+
2. **Provider Pattern**: Centralized state management (recommended)
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Hook-Only Usage For Light and Dark mode animation
|
|
36
|
+
|
|
37
|
+
Perfect for custom theme toggle buttons and complete control over animated theme logic.
|
|
38
|
+
|
|
39
|
+
### Basic Theme Toggle
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { useThemeAnimation } from '@spaceman/react-theme-animation';
|
|
43
|
+
|
|
44
|
+
function ThemeToggle() {
|
|
45
|
+
const { theme, toggleTheme, ref } = useThemeAnimation();
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<button ref={ref} onClick={toggleTheme} className="theme-toggle-btn">
|
|
49
|
+
{theme === 'light' ? '🌙' : '🌞'} {theme}
|
|
50
|
+
</button>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2. Provider Pattern (Recommended)
|
|
58
|
+
|
|
59
|
+
The most powerful pattern using `SpacemanThemeProvider` for centralized theme state management. This prevents state drift and provides synchronized animations.
|
|
60
|
+
|
|
61
|
+
### Basic Provider Setup
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import {
|
|
65
|
+
SpacemanThemeProvider,
|
|
66
|
+
ThemeSwitcher,
|
|
67
|
+
ThemeSelector,
|
|
68
|
+
} from '@spaceman/react-theme-animation';
|
|
69
|
+
|
|
70
|
+
function App() {
|
|
71
|
+
return (
|
|
72
|
+
<SpacemanThemeProvider>
|
|
73
|
+
<div className="app">
|
|
74
|
+
<header>
|
|
75
|
+
<ThemeSwitcher />
|
|
76
|
+
</header>
|
|
77
|
+
|
|
78
|
+
<aside>
|
|
79
|
+
<ThemeSelector colorThemes={['default', 'blue', 'green', 'purple']} />
|
|
80
|
+
</aside>
|
|
81
|
+
|
|
82
|
+
<main>
|
|
83
|
+
<YourAppContent />
|
|
84
|
+
</main>
|
|
85
|
+
</div>
|
|
86
|
+
</SpacemanThemeProvider>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Advanced Provider Configuration
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import {
|
|
95
|
+
SpacemanThemeProvider,
|
|
96
|
+
ThemeSwitcher,
|
|
97
|
+
ThemeSelector,
|
|
98
|
+
ThemeAnimationType,
|
|
99
|
+
} from '@spaceman/react-theme-animation';
|
|
100
|
+
|
|
101
|
+
function App() {
|
|
102
|
+
return (
|
|
103
|
+
<SpacemanThemeProvider
|
|
104
|
+
defaultTheme="system"
|
|
105
|
+
defaultColorTheme="blue"
|
|
106
|
+
themes={['light', 'dark', 'system']}
|
|
107
|
+
colorThemes={['default', 'blue', 'green', 'purple', 'red']}
|
|
108
|
+
animationType={ThemeAnimationType.BLUR_CIRCLE}
|
|
109
|
+
blurAmount={10}
|
|
110
|
+
duration={1000}
|
|
111
|
+
onThemeChange={theme => {
|
|
112
|
+
// Analytics, localStorage, etc.
|
|
113
|
+
console.log('Global theme changed:', theme);
|
|
114
|
+
}}
|
|
115
|
+
onColorThemeChange={colorTheme => {
|
|
116
|
+
console.log('Global color theme changed:', colorTheme);
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
<AppLayout />
|
|
120
|
+
</SpacemanThemeProvider>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function AppLayout() {
|
|
125
|
+
return (
|
|
126
|
+
<div className="app-layout">
|
|
127
|
+
<Header />
|
|
128
|
+
<Sidebar />
|
|
129
|
+
<MainContent />
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function Header() {
|
|
135
|
+
return (
|
|
136
|
+
<header className="header">
|
|
137
|
+
<h1>My App</h1>
|
|
138
|
+
<ThemeSwitcher className="ml-auto" />
|
|
139
|
+
</header>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function Sidebar() {
|
|
144
|
+
return (
|
|
145
|
+
<aside className="sidebar">
|
|
146
|
+
<h2>Settings</h2>
|
|
147
|
+
<ThemeSelector colorThemes={['default', 'blue', 'green', 'purple', 'red']} />
|
|
148
|
+
</aside>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Using the Provider Hook
|
|
154
|
+
|
|
155
|
+
Access theme state anywhere in your component tree. This hook also enables you to set a reference point for the animation to start from.
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
import { useSpacemanTheme } from '@spaceman/react-theme-animation';
|
|
159
|
+
|
|
160
|
+
function ThemeStatus() {
|
|
161
|
+
const { theme, colorTheme, switchTheme, setColorTheme, switchThemeFromElement } =
|
|
162
|
+
useSpacemanTheme();
|
|
163
|
+
|
|
164
|
+
const handleCustomThemeSwitch = event => {
|
|
165
|
+
// Animate from the clicked element
|
|
166
|
+
switchThemeFromElement('dark', event.currentTarget);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<div className="theme-status">
|
|
171
|
+
<p>Current theme: {theme}</p>
|
|
172
|
+
<p>Current color: {colorTheme}</p>
|
|
173
|
+
|
|
174
|
+
<button onClick={handleCustomThemeSwitch}>Switch to Dark (with animation)</button>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Complete Example: Advanced Theme System
|
|
183
|
+
|
|
184
|
+
Here's a complete example showcasing all features:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import React from 'react';
|
|
188
|
+
import {
|
|
189
|
+
SpacemanThemeProvider,
|
|
190
|
+
ThemeSwitcher,
|
|
191
|
+
ThemeSelector,
|
|
192
|
+
useSpacemanTheme,
|
|
193
|
+
ThemeAnimationType,
|
|
194
|
+
} from '@spaceman/react-theme-animation';
|
|
195
|
+
|
|
196
|
+
// Main App with Provider
|
|
197
|
+
function App() {
|
|
198
|
+
return (
|
|
199
|
+
<SpacemanThemeProvider
|
|
200
|
+
defaultTheme="system"
|
|
201
|
+
defaultColorTheme="blue"
|
|
202
|
+
themes={['light', 'dark', 'system']}
|
|
203
|
+
colorThemes={['default', 'blue', 'green', 'purple', 'red', 'orange']}
|
|
204
|
+
animationType={ThemeAnimationType.CIRCLE}
|
|
205
|
+
duration={800}
|
|
206
|
+
onThemeChange={theme => {
|
|
207
|
+
// Save to localStorage, analytics, etc.
|
|
208
|
+
localStorage.setItem('preferred-theme', theme);
|
|
209
|
+
}}
|
|
210
|
+
onColorThemeChange={colorTheme => {
|
|
211
|
+
localStorage.setItem('preferred-color-theme', colorTheme);
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
214
|
+
<AppContent />
|
|
215
|
+
</SpacemanThemeProvider>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// App Layout
|
|
220
|
+
function AppContent() {
|
|
221
|
+
return (
|
|
222
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
223
|
+
<Header />
|
|
224
|
+
<div className="container mx-auto px-4 py-8">
|
|
225
|
+
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
|
226
|
+
<Sidebar />
|
|
227
|
+
<MainContent />
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Header with Theme Switcher
|
|
235
|
+
function Header() {
|
|
236
|
+
return (
|
|
237
|
+
<header className="border-b bg-card">
|
|
238
|
+
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
|
|
239
|
+
<h1 className="text-2xl font-bold">Theme Demo</h1>
|
|
240
|
+
<div className="flex items-center gap-4">
|
|
241
|
+
<ThemeSwitcher className="rounded-lg" />
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</header>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Sidebar with Theme Selector
|
|
249
|
+
function Sidebar() {
|
|
250
|
+
return (
|
|
251
|
+
<aside className="space-y-6">
|
|
252
|
+
<div className="bg-card p-6 rounded-lg border">
|
|
253
|
+
<h2 className="text-lg font-semibold mb-4">Theme Settings</h2>
|
|
254
|
+
<div className="space-y-4">
|
|
255
|
+
<div>
|
|
256
|
+
<label className="text-sm font-medium mb-2 block">Color Theme</label>
|
|
257
|
+
<ThemeSelector colorThemes={['default', 'blue', 'green', 'purple', 'red', 'orange']} />
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
|
|
262
|
+
<ThemeInfo />
|
|
263
|
+
</aside>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Component using the theme hook
|
|
268
|
+
function ThemeInfo() {
|
|
269
|
+
const { theme, colorTheme, switchThemeFromElement } = useSpacemanTheme();
|
|
270
|
+
|
|
271
|
+
const quickSwitchTheme = (newTheme, event) => {
|
|
272
|
+
switchThemeFromElement(newTheme, event.currentTarget);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<div className="bg-card p-6 rounded-lg border">
|
|
277
|
+
<h3 className="text-lg font-semibold mb-4">Current State</h3>
|
|
278
|
+
<div className="space-y-2 text-sm">
|
|
279
|
+
<p>
|
|
280
|
+
<strong>Theme:</strong> {theme}
|
|
281
|
+
</p>
|
|
282
|
+
<p>
|
|
283
|
+
<strong>Color:</strong> {colorTheme}
|
|
284
|
+
</p>
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
<div className="mt-4 space-y-2">
|
|
288
|
+
<p className="text-sm font-medium">Quick Switch:</p>
|
|
289
|
+
<div className="flex gap-2">
|
|
290
|
+
{['light', 'dark'].map(t => (
|
|
291
|
+
<button
|
|
292
|
+
key={t}
|
|
293
|
+
onClick={e => quickSwitchTheme(t, e)}
|
|
294
|
+
className="px-3 py-1 text-xs bg-primary text-primary-foreground rounded hover:bg-primary/90 transition-colors"
|
|
295
|
+
>
|
|
296
|
+
{t}
|
|
297
|
+
</button>
|
|
298
|
+
))}
|
|
299
|
+
</div>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Main content area
|
|
306
|
+
function MainContent() {
|
|
307
|
+
return (
|
|
308
|
+
<main className="lg:col-span-3 space-y-6">
|
|
309
|
+
<div className="bg-card p-6 rounded-lg border">
|
|
310
|
+
<h2 className="text-xl font-semibold mb-4">Welcome to Theme Demo</h2>
|
|
311
|
+
<p className="text-muted-foreground">
|
|
312
|
+
This demo showcases the spaceman-react-theme-animation library with synchronized theme
|
|
313
|
+
state, smooth animations, and multiple color themes.
|
|
314
|
+
</p>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
318
|
+
{[1, 2, 3, 4].map(i => (
|
|
319
|
+
<div key={i} className="bg-card p-6 rounded-lg border">
|
|
320
|
+
<h3 className="font-semibold mb-2">Card {i}</h3>
|
|
321
|
+
<p className="text-sm text-muted-foreground">
|
|
322
|
+
Sample content that adapts to the current theme and color scheme.
|
|
323
|
+
</p>
|
|
324
|
+
</div>
|
|
325
|
+
))}
|
|
326
|
+
</div>
|
|
327
|
+
</main>
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export default App;
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## API Reference
|
|
337
|
+
|
|
338
|
+
### SpacemanThemeProvider
|
|
339
|
+
|
|
340
|
+
Context provider for centralized theme state management.
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
<SpacemanThemeProvider
|
|
344
|
+
defaultTheme="system"
|
|
345
|
+
defaultColorTheme="blue"
|
|
346
|
+
themes={['light', 'dark', 'system']}
|
|
347
|
+
colorThemes={['default', 'blue', 'green', 'purple']}
|
|
348
|
+
animationType={ThemeAnimationType.CIRCLE}
|
|
349
|
+
duration={800}
|
|
350
|
+
onThemeChange={theme => console.log('Theme:', theme)}
|
|
351
|
+
onColorThemeChange={colorTheme => console.log('Color:', colorTheme)}
|
|
352
|
+
>
|
|
353
|
+
<App />
|
|
354
|
+
</SpacemanThemeProvider>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### SpacemanThemeProvider Props
|
|
358
|
+
|
|
359
|
+
| Property | Type | Default | Description |
|
|
360
|
+
| -------------------- | ---------------------------------- | ----------------------------- | ------------------------------------- |
|
|
361
|
+
| `defaultTheme` | `Theme` | `'system'` | Initial theme |
|
|
362
|
+
| `defaultColorTheme` | `ColorTheme` | `'default'` | Initial color theme |
|
|
363
|
+
| `themes` | `Theme[]` | `['light', 'dark', 'system']` | Available themes |
|
|
364
|
+
| `colorThemes` | `ColorTheme[]` | `['default']` | Available color themes |
|
|
365
|
+
| `animationType` | `ThemeAnimationType` | `ThemeAnimationType.CIRCLE` | Animation type |
|
|
366
|
+
| `duration` | `number` | `500` | Animation duration in ms |
|
|
367
|
+
| `blurAmount` | `number` | `2` | Blur amount for blur-circle animation |
|
|
368
|
+
| `onThemeChange` | `(theme: Theme) => void` | - | Global theme change callback |
|
|
369
|
+
| `onColorThemeChange` | `(colorTheme: ColorTheme) => void` | - | Global color theme change callback |
|
|
370
|
+
|
|
371
|
+
### useSpacemanTheme
|
|
372
|
+
|
|
373
|
+
Hook to access theme state from SpacemanThemeProvider context.
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
const { theme, colorTheme, switchTheme, setColorTheme, switchThemeFromElement, ref } =
|
|
377
|
+
useSpacemanTheme();
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### Returns
|
|
381
|
+
|
|
382
|
+
| Property | Type | Description |
|
|
383
|
+
| ------------------------ | ------------------------------------------------------- | ------------------------------------------------- |
|
|
384
|
+
| `theme` | `Theme` | Current theme |
|
|
385
|
+
| `colorTheme` | `ColorTheme` | Current color theme |
|
|
386
|
+
| `switchTheme` | `(theme: Theme) => Promise<void>` | Switch to specific theme |
|
|
387
|
+
| `setColorTheme` | `(colorTheme: ColorTheme) => void` | Set color theme |
|
|
388
|
+
| `switchThemeFromElement` | `(theme: Theme, element: HTMLElement) => Promise<void>` | Switch theme with animation from specific element |
|
|
389
|
+
| `ref` | `RefObject<HTMLElement>` | Ref for animation origin |
|
|
390
|
+
|
|
391
|
+
### useThemeAnimation
|
|
392
|
+
|
|
393
|
+
Standalone hook for theme management and animations.
|
|
394
|
+
|
|
395
|
+
```tsx
|
|
396
|
+
const { theme, colorTheme, switchTheme, setColorTheme, toggleTheme, ref } = useThemeAnimation({
|
|
397
|
+
themes: ['light', 'dark', 'system'],
|
|
398
|
+
colorThemes: ['default', 'blue', 'green', 'purple'],
|
|
399
|
+
animationType: ThemeAnimationType.SCALE,
|
|
400
|
+
duration: 800,
|
|
401
|
+
onThemeChange: theme => console.log('Theme changed:', theme),
|
|
402
|
+
onColorThemeChange: colorTheme => console.log('Color changed:', colorTheme),
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
#### Options
|
|
407
|
+
|
|
408
|
+
| Property | Type | Default | Description |
|
|
409
|
+
| -------------------- | ---------------------------------- | ----------------------------- | ------------------------------------- |
|
|
410
|
+
| `themes` | `Theme[]` | `['light', 'dark', 'system']` | Available themes |
|
|
411
|
+
| `colorThemes` | `ColorTheme[]` | `['default']` | Available color themes |
|
|
412
|
+
| `theme` | `Theme` | `'system'` | Initial theme |
|
|
413
|
+
| `colorTheme` | `ColorTheme` | `'default'` | Initial color theme |
|
|
414
|
+
| `animationType` | `ThemeAnimationType` | `ThemeAnimationType.CIRCLE` | Animation type |
|
|
415
|
+
| `duration` | `number` | `500` | Animation duration in ms |
|
|
416
|
+
| `blurAmount` | `number` | `2` | Blur amount for blur-circle animation |
|
|
417
|
+
| `onThemeChange` | `(theme: Theme) => void` | - | Theme change callback |
|
|
418
|
+
| `onColorThemeChange` | `(colorTheme: ColorTheme) => void` | - | Color theme change callback |
|
|
419
|
+
|
|
420
|
+
#### Return
|
|
421
|
+
|
|
422
|
+
| Property | Type | Description |
|
|
423
|
+
| --------------- | ---------------------------------- | ------------------------- |
|
|
424
|
+
| `theme` | `Theme` | Current theme |
|
|
425
|
+
| `colorTheme` | `ColorTheme` | Current color theme |
|
|
426
|
+
| `switchTheme` | `(theme: Theme) => Promise<void>` | Switch to specific theme |
|
|
427
|
+
| `setColorTheme` | `(colorTheme: ColorTheme) => void` | Set color theme |
|
|
428
|
+
| `toggleTheme` | `() => Promise<void>` | Toggle between light/dark |
|
|
429
|
+
| `ref` | `RefObject<HTMLElement>` | Ref for animation origin |
|
|
430
|
+
|
|
431
|
+
### ThemeSwitcher
|
|
432
|
+
|
|
433
|
+
Pre-built theme switcher component with animated buttons.
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
<ThemeSwitcher
|
|
437
|
+
themes={['light', 'dark', 'system']}
|
|
438
|
+
currentTheme="light"
|
|
439
|
+
onThemeChange={theme => console.log(theme)}
|
|
440
|
+
animationType={ThemeAnimationType.CIRCLE}
|
|
441
|
+
duration={600}
|
|
442
|
+
className="custom-class"
|
|
443
|
+
/>
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
#### ThemeSwitcher Props
|
|
447
|
+
|
|
448
|
+
| Property | Type | Default | Description |
|
|
449
|
+
| --------------- | ------------------------ | ----------------------------- | ----------------------------------- |
|
|
450
|
+
| `themes` | `Theme[]` | `['light', 'dark', 'system']` | Available themes |
|
|
451
|
+
| `currentTheme` | `Theme` | - | Controlled current theme (optional) |
|
|
452
|
+
| `onThemeChange` | `(theme: Theme) => void` | - | Theme change callback (optional) |
|
|
453
|
+
| `animationType` | `ThemeAnimationType` | `ThemeAnimationType.CIRCLE` | Animation type |
|
|
454
|
+
| `duration` | `number` | `500` | Animation duration in ms |
|
|
455
|
+
| `className` | `string` | - | Additional CSS classes |
|
|
456
|
+
|
|
457
|
+
> **Note**: When used with `SpacemanThemeProvider`, `currentTheme` and `onThemeChange` are automatically handled by the context.
|
|
458
|
+
|
|
459
|
+
### ThemeSelector
|
|
460
|
+
|
|
461
|
+
Dropdown selector for color themes.
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
<ThemeSelector
|
|
465
|
+
colorThemes={['default', 'blue', 'green', 'purple']}
|
|
466
|
+
currentColorTheme="blue"
|
|
467
|
+
onColorThemeChange={colorTheme => console.log(colorTheme)}
|
|
468
|
+
animationType={ThemeAnimationType.BLUR_CIRCLE}
|
|
469
|
+
duration={400}
|
|
470
|
+
/>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
#### ThemeSelector Props
|
|
474
|
+
|
|
475
|
+
| Property | Type | Default | Description |
|
|
476
|
+
| -------------------- | ---------------------------------- | ----------------------------- | ----------------------------------------- |
|
|
477
|
+
| `themes` | `Theme[]` | `['light', 'dark', 'system']` | Available themes (for standalone hook) |
|
|
478
|
+
| `colorThemes` | `ColorTheme[]` | `['default']` | Available color themes |
|
|
479
|
+
| `currentColorTheme` | `ColorTheme` | - | Controlled current color theme (optional) |
|
|
480
|
+
| `onColorThemeChange` | `(colorTheme: ColorTheme) => void` | - | Color theme change callback (optional) |
|
|
481
|
+
| `animationType` | `ThemeAnimationType` | `ThemeAnimationType.CIRCLE` | Animation type |
|
|
482
|
+
| `duration` | `number` | `500` | Animation duration in ms |
|
|
483
|
+
|
|
484
|
+
> **Note**: When used with `SpacemanThemeProvider`, `currentColorTheme` and `onColorThemeChange` are automatically handled by the context.
|
|
485
|
+
|
|
486
|
+
### ThemeAnimationType
|
|
487
|
+
|
|
488
|
+
Animation types for theme transitions.
|
|
489
|
+
|
|
490
|
+
```tsx
|
|
491
|
+
enum ThemeAnimationType {
|
|
492
|
+
CIRCLE = 'circle',
|
|
493
|
+
BLUR_CIRCLE = 'blur-circle',
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Types
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
type Theme = 'light' | 'dark' | 'system';
|
|
501
|
+
type ColorTheme = string; // e.g., 'default', 'blue', 'green', etc.
|
|
502
|
+
|
|
503
|
+
interface SpacemanThemeProviderProps {
|
|
504
|
+
children: React.ReactNode;
|
|
505
|
+
defaultTheme?: Theme;
|
|
506
|
+
defaultColorTheme?: ColorTheme;
|
|
507
|
+
themes?: Theme[];
|
|
508
|
+
colorThemes?: ColorTheme[];
|
|
509
|
+
animationType?: ThemeAnimationType;
|
|
510
|
+
duration?: number;
|
|
511
|
+
blurAmount?: number;
|
|
512
|
+
onThemeChange?: (theme: Theme) => void;
|
|
513
|
+
onColorThemeChange?: (colorTheme: ColorTheme) => void;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
interface UseThemeAnimationProps {
|
|
517
|
+
themes?: Theme[];
|
|
518
|
+
colorThemes?: ColorTheme[];
|
|
519
|
+
theme?: Theme;
|
|
520
|
+
colorTheme?: ColorTheme;
|
|
521
|
+
animationType?: ThemeAnimationType;
|
|
522
|
+
duration?: number;
|
|
523
|
+
blurAmount?: number;
|
|
524
|
+
onThemeChange?: (theme: Theme) => void;
|
|
525
|
+
onColorThemeChange?: (colorTheme: ColorTheme) => void;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
interface UseThemeAnimationReturn {
|
|
529
|
+
theme: Theme;
|
|
530
|
+
colorTheme: ColorTheme;
|
|
531
|
+
switchTheme: (theme: Theme) => Promise<void>;
|
|
532
|
+
setColorTheme: (colorTheme: ColorTheme) => void;
|
|
533
|
+
toggleTheme: () => Promise<void>;
|
|
534
|
+
ref: RefObject<HTMLElement>;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
interface ThemeSwitcherProps {
|
|
538
|
+
themes?: Theme[];
|
|
539
|
+
currentTheme?: Theme;
|
|
540
|
+
onThemeChange?: (theme: Theme) => void;
|
|
541
|
+
animationType?: ThemeAnimationType;
|
|
542
|
+
duration?: number;
|
|
543
|
+
className?: string;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
interface ThemeSelectorProps {
|
|
547
|
+
themes?: Theme[];
|
|
548
|
+
colorThemes?: ColorTheme[];
|
|
549
|
+
currentColorTheme?: ColorTheme;
|
|
550
|
+
onColorThemeChange?: (colorTheme: ColorTheme) => void;
|
|
551
|
+
animationType?: ThemeAnimationType;
|
|
552
|
+
duration?: number;
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## CSS Variables
|
|
559
|
+
|
|
560
|
+
The library uses CSS custom properties for theming. Define these in your CSS:
|
|
561
|
+
|
|
562
|
+
```css
|
|
563
|
+
/* Base theme variables */
|
|
564
|
+
:root {
|
|
565
|
+
--background: 0 0% 100%;
|
|
566
|
+
--foreground: 222.2 84% 4.9%;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/* Dark theme */
|
|
570
|
+
.dark {
|
|
571
|
+
--background: 222.2 84% 4.9%;
|
|
572
|
+
--foreground: 210 40% 98%;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* Add other theme variants from design system or tweakcn */
|
|
576
|
+
|
|
577
|
+
.theme-supabase {
|
|
578
|
+
--background: oklch(0.9911 0 0);
|
|
579
|
+
--foreground: oklch(0.2046 0 0);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.theme-supabase.dark {
|
|
583
|
+
--background: oklch(0.1822 0 0);
|
|
584
|
+
--foreground: oklch(0.9288 0.0126 255.5078);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.theme-mono {
|
|
588
|
+
--background: oklch(1 0 0);
|
|
589
|
+
--foreground: oklch(0.1448 0 0);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.theme-mono.dark {
|
|
593
|
+
--background: oklch(0.1448 0 0);
|
|
594
|
+
--foreground: oklch(1 0 0);
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## Browser Support
|
|
601
|
+
|
|
602
|
+
- **View Transitions API**: Chrome 111+, Edge 111+
|
|
603
|
+
- **Fallback**: All modern browsers with CSS transitions
|
|
604
|
+
- **Reduced Motion**: Respects `prefers-reduced-motion` setting
|
|
605
|
+
- **Framework Support**: React 16.8+ (hooks required)
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Performance
|
|
610
|
+
|
|
611
|
+
- Animations use the View Transitions API when available
|
|
612
|
+
- Optimized for 60fps animations
|
|
613
|
+
- Respects `prefers-reduced-motion` setting
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Contributing
|
|
618
|
+
|
|
619
|
+
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
|
|
620
|
+
|
|
621
|
+
## License
|
|
622
|
+
|
|
623
|
+
MIT
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { Theme, ColorTheme, ThemeAnimationType } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Context type for the Spaceman Theme Provider
|
|
5
|
+
*/
|
|
6
|
+
interface SpacemanThemeContextType {
|
|
7
|
+
theme: Theme;
|
|
8
|
+
colorTheme: ColorTheme;
|
|
9
|
+
resolvedTheme: 'light' | 'dark';
|
|
10
|
+
setTheme: (theme: Theme) => void;
|
|
11
|
+
setColorTheme: (colorTheme: ColorTheme) => void;
|
|
12
|
+
toggleTheme: () => Promise<void>;
|
|
13
|
+
switchTheme: (theme: Theme) => Promise<void>;
|
|
14
|
+
switchThemeFromElement: (theme: Theme, element: HTMLButtonElement) => Promise<void>;
|
|
15
|
+
ref: React.RefObject<HTMLButtonElement | null>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Props for the Spaceman Theme Provider
|
|
19
|
+
*/
|
|
20
|
+
interface SpacemanThemeProviderProps {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
themes?: Theme[];
|
|
23
|
+
colorThemes?: ColorTheme[];
|
|
24
|
+
defaultTheme?: Theme;
|
|
25
|
+
defaultColorTheme?: ColorTheme;
|
|
26
|
+
animationType?: ThemeAnimationType;
|
|
27
|
+
duration?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Spaceman Theme Provider - Provides centralized theme state management
|
|
31
|
+
*
|
|
32
|
+
* This provider creates a single source of truth for theme state using the useThemeAnimation hook.
|
|
33
|
+
* All theme-related components should consume from this context to ensure synchronization.
|
|
34
|
+
*
|
|
35
|
+
* @param children - React children to wrap with theme context
|
|
36
|
+
* @param themes - Available theme options (default: ['light', 'dark', 'system'])
|
|
37
|
+
* @param colorThemes - Available color theme options (default: ['default'])
|
|
38
|
+
* @param defaultTheme - Default theme to use (default: 'system')
|
|
39
|
+
* @param defaultColorTheme - Default color theme to use (default: 'default')
|
|
40
|
+
* @param animationType - Animation type for theme transitions (default: CIRCLE)
|
|
41
|
+
* @param duration - Animation duration in milliseconds (default: 750)
|
|
42
|
+
*/
|
|
43
|
+
export declare const SpacemanThemeProvider: React.FC<SpacemanThemeProviderProps>;
|
|
44
|
+
/**
|
|
45
|
+
* Hook to consume the Spaceman Theme context
|
|
46
|
+
*
|
|
47
|
+
* @returns Theme context value with all theme state and methods
|
|
48
|
+
* @throws Error if used outside of SpacemanThemeProvider
|
|
49
|
+
*/
|
|
50
|
+
export declare const useSpacemanTheme: () => SpacemanThemeContextType;
|
|
51
|
+
export {};
|