@fakhrirafiki/theme-engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +566 -0
- package/dist/index.d.mts +501 -0
- package/dist/index.d.ts +501 -0
- package/dist/index.js +4696 -0
- package/dist/index.mjs +4653 -0
- package/dist/styles/animations.css +103 -0
- package/dist/styles/base.css +151 -0
- package/dist/styles/components.css +135 -0
- package/dist/styles/index.css +43 -0
- package/dist/styles/tailwind.css +105 -0
- package/dist/styles/utilities.css +226 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# 🎨 Theme Engine
|
|
2
|
+
|
|
3
|
+
Elegant theming system with smooth transitions and custom color presets for modern React applications.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- 🌓 **Unified Theming** - Seamlessly coordinate dark/light mode with custom color presets
|
|
8
|
+
- 🎨 **Beautiful Presets** - 9 curated color themes with smooth animated selection
|
|
9
|
+
- ✨ **Smooth Transitions** - Ripple effects using View Transition API
|
|
10
|
+
- 💾 **Persistent** - localStorage integration with SSR-safe restoration
|
|
11
|
+
- ♿ **Accessible** - Full ARIA support and reduced motion compatibility
|
|
12
|
+
- 🚀 **Zero Config** - Works out of the box with sensible defaults
|
|
13
|
+
- 📦 **Lightweight** - Optimized bundle size and direct source consumption
|
|
14
|
+
|
|
15
|
+
## 🚀 Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install from npm
|
|
19
|
+
npm install theme-engine
|
|
20
|
+
|
|
21
|
+
# Or with yarn/pnpm
|
|
22
|
+
yarn add theme-engine
|
|
23
|
+
pnpm add theme-engine
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { ThemeProvider, ThemeScript, ThemeToggle } from 'theme-engine'
|
|
28
|
+
import 'theme-engine/styles'
|
|
29
|
+
|
|
30
|
+
function App() {
|
|
31
|
+
return (
|
|
32
|
+
<html lang="en" suppressHydrationWarning>
|
|
33
|
+
<head>
|
|
34
|
+
<ThemeScript
|
|
35
|
+
presetStorageKey="my-app-theme-preset"
|
|
36
|
+
defaultPreset="twitter"
|
|
37
|
+
/>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<ThemeProvider
|
|
41
|
+
defaultMode="system"
|
|
42
|
+
defaultPreset="twitter"
|
|
43
|
+
modeStorageKey="my-app-mode"
|
|
44
|
+
presetStorageKey="my-app-theme-preset"
|
|
45
|
+
enablePresets={true}
|
|
46
|
+
>
|
|
47
|
+
<header>
|
|
48
|
+
<ThemeToggle />
|
|
49
|
+
</header>
|
|
50
|
+
<main>
|
|
51
|
+
Your app content
|
|
52
|
+
</main>
|
|
53
|
+
</ThemeProvider>
|
|
54
|
+
</body>
|
|
55
|
+
</html>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 🎨 Theme Presets
|
|
61
|
+
|
|
62
|
+
Add beautiful color presets with smooth animations:
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { ThemePresetButtons } from 'theme-engine'
|
|
66
|
+
|
|
67
|
+
function ThemeSelector() {
|
|
68
|
+
return (
|
|
69
|
+
<ThemePresetButtons
|
|
70
|
+
animation={{
|
|
71
|
+
enabled: true,
|
|
72
|
+
duration: 4,
|
|
73
|
+
rowCount: 2,
|
|
74
|
+
scrollSpeed: 1,
|
|
75
|
+
}}
|
|
76
|
+
layout={{
|
|
77
|
+
buttonWidth: 140,
|
|
78
|
+
buttonGap: 12,
|
|
79
|
+
showColorBoxes: true,
|
|
80
|
+
colorBoxCount: 3,
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 📦 Core Components
|
|
88
|
+
|
|
89
|
+
### ThemeProvider
|
|
90
|
+
|
|
91
|
+
The heart of the theming system - coordinates dark/light mode with custom presets elegantly.
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
<ThemeProvider
|
|
95
|
+
defaultMode="system" // "light" | "dark" | "system"
|
|
96
|
+
defaultPreset="twitter" // Default preset ID to apply
|
|
97
|
+
modeStorageKey="app-mode" // localStorage key for theme mode
|
|
98
|
+
presetStorageKey="app-preset" // localStorage key for color preset
|
|
99
|
+
enablePresets={true} // Enable preset functionality
|
|
100
|
+
>
|
|
101
|
+
{children}
|
|
102
|
+
</ThemeProvider>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Custom Presets
|
|
106
|
+
|
|
107
|
+
Add your own theme presets alongside the built-in TweakCN collection:
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
import { ThemeProvider, type TweakCNThemePreset } from 'theme-engine'
|
|
111
|
+
|
|
112
|
+
const customPresets: Record<string, TweakCNThemePreset> = {
|
|
113
|
+
"my-brand": {
|
|
114
|
+
label: "My Brand Theme",
|
|
115
|
+
styles: {
|
|
116
|
+
light: {
|
|
117
|
+
background: "#ffffff",
|
|
118
|
+
foreground: "#1a1a1a",
|
|
119
|
+
primary: "#0066cc",
|
|
120
|
+
"primary-foreground": "#ffffff",
|
|
121
|
+
secondary: "#f0f8ff",
|
|
122
|
+
"secondary-foreground": "#004499",
|
|
123
|
+
// ... all 36+ CSS properties
|
|
124
|
+
border: "#e2e8f0",
|
|
125
|
+
input: "#f1f5f9",
|
|
126
|
+
ring: "#0066cc",
|
|
127
|
+
radius: "0.5rem",
|
|
128
|
+
},
|
|
129
|
+
dark: {
|
|
130
|
+
background: "#0a0a0a",
|
|
131
|
+
foreground: "#fafafa",
|
|
132
|
+
primary: "#3399ff",
|
|
133
|
+
"primary-foreground": "#000000",
|
|
134
|
+
// ... all 36+ CSS properties
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"retro-gaming": {
|
|
139
|
+
label: "Retro Gaming",
|
|
140
|
+
styles: {
|
|
141
|
+
light: {
|
|
142
|
+
background: "#f5f5dc",
|
|
143
|
+
foreground: "#2a1810",
|
|
144
|
+
primary: "#ff6b35",
|
|
145
|
+
"primary-foreground": "#ffffff",
|
|
146
|
+
accent: "#06ffa5",
|
|
147
|
+
"accent-foreground": "#2a1810",
|
|
148
|
+
// ... complete theme definition
|
|
149
|
+
},
|
|
150
|
+
dark: {
|
|
151
|
+
background: "#1a1a2e",
|
|
152
|
+
foreground: "#ffd23f",
|
|
153
|
+
primary: "#ff6b35",
|
|
154
|
+
"primary-foreground": "#000000",
|
|
155
|
+
// ... complete theme definition
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function App() {
|
|
162
|
+
return (
|
|
163
|
+
<ThemeProvider
|
|
164
|
+
customPresets={customPresets}
|
|
165
|
+
defaultPreset="my-brand"
|
|
166
|
+
>
|
|
167
|
+
{/* Custom presets automatically appear in ThemePresetButtons */}
|
|
168
|
+
<ThemePresetButtons
|
|
169
|
+
showBuiltIn={true}
|
|
170
|
+
showCustom={true}
|
|
171
|
+
groupBy="provider"
|
|
172
|
+
labels={{
|
|
173
|
+
builtIn: "Built-in Themes",
|
|
174
|
+
custom: "🎨 Brand Themes"
|
|
175
|
+
}}
|
|
176
|
+
/>
|
|
177
|
+
</ThemeProvider>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Preset Validation
|
|
183
|
+
|
|
184
|
+
Validate your custom presets before using them to ensure they work correctly:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { validateCustomPresets, logValidationResult } from 'theme-engine'
|
|
188
|
+
|
|
189
|
+
const customPresets = {
|
|
190
|
+
"my-theme": {
|
|
191
|
+
label: "My Theme",
|
|
192
|
+
styles: {
|
|
193
|
+
light: { /* theme properties */ },
|
|
194
|
+
dark: { /* theme properties */ }
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Validate presets
|
|
200
|
+
const validationResult = validateCustomPresets(customPresets);
|
|
201
|
+
|
|
202
|
+
if (validationResult.isValid) {
|
|
203
|
+
console.log('✅ All presets are valid!');
|
|
204
|
+
} else {
|
|
205
|
+
console.error('❌ Invalid presets:', validationResult.errors);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Log detailed validation results
|
|
209
|
+
logValidationResult(validationResult, 'My Custom Presets');
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Required Properties** (minimum for a valid preset):
|
|
213
|
+
- `background`, `foreground`
|
|
214
|
+
- `primary`, `primary-foreground`
|
|
215
|
+
- `secondary`, `secondary-foreground`
|
|
216
|
+
- `card`, `card-foreground`
|
|
217
|
+
|
|
218
|
+
**Recommended Properties** for complete themes:
|
|
219
|
+
- `border`, `input`, `ring`
|
|
220
|
+
- `muted`, `muted-foreground`
|
|
221
|
+
- `accent`, `accent-foreground`
|
|
222
|
+
- `destructive`, `destructive-foreground`
|
|
223
|
+
- All 36+ CSS properties for full compatibility
|
|
224
|
+
|
|
225
|
+
### ThemeScript
|
|
226
|
+
|
|
227
|
+
Pre-hydration script for seamless SSR theme restoration. Place in `<head>`:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
<ThemeScript
|
|
231
|
+
presetStorageKey="app-preset"
|
|
232
|
+
defaultPreset="twitter" // Apply default preset if none stored
|
|
233
|
+
/>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### ThemeToggle
|
|
237
|
+
|
|
238
|
+
Beautiful dark/light mode toggle with ripple animation:
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
<ThemeToggle
|
|
242
|
+
size="sm" // "sm" | "md" | "lg"
|
|
243
|
+
variant="ghost" // "default" | "outline" | "ghost"
|
|
244
|
+
className="custom-class"
|
|
245
|
+
/>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### ThemePresetButtons
|
|
249
|
+
|
|
250
|
+
Zero-config animated preset selector with beautiful color previews. State management is handled automatically via ThemeProvider context:
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
{/* Basic usage - shows all presets */}
|
|
254
|
+
<ThemePresetButtons />
|
|
255
|
+
|
|
256
|
+
{/* Advanced usage with categorization */}
|
|
257
|
+
<ThemePresetButtons
|
|
258
|
+
categories={["nature", "vibrant"]} // Filter presets by category
|
|
259
|
+
maxPresets={20} // Limit number of presets shown
|
|
260
|
+
showBuiltIn={true} // Show built-in presets (default: true)
|
|
261
|
+
showCustom={true} // Show custom presets (default: true)
|
|
262
|
+
groupBy="provider" // Group by: 'none' | 'category' | 'provider'
|
|
263
|
+
labels={{
|
|
264
|
+
builtIn: "Built-in Themes",
|
|
265
|
+
custom: "🎨 Your Themes"
|
|
266
|
+
}}
|
|
267
|
+
showSectionHeaders={true} // Show section headers when grouping
|
|
268
|
+
animation={{
|
|
269
|
+
enabled: true,
|
|
270
|
+
duration: 4, // Animation duration in seconds
|
|
271
|
+
rowCount: 2, // Number of animation rows
|
|
272
|
+
scrollSpeed: 1, // Scroll speed multiplier
|
|
273
|
+
}}
|
|
274
|
+
layout={{
|
|
275
|
+
buttonWidth: 140,
|
|
276
|
+
buttonGap: 12,
|
|
277
|
+
rowGap: 12,
|
|
278
|
+
showColorBoxes: true,
|
|
279
|
+
colorBoxCount: 3,
|
|
280
|
+
}}
|
|
281
|
+
/>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## 🎣 Hooks
|
|
285
|
+
|
|
286
|
+
### useTheme
|
|
287
|
+
|
|
288
|
+
Access both theme mode and preset state:
|
|
289
|
+
|
|
290
|
+
```tsx
|
|
291
|
+
function ThemeStatus() {
|
|
292
|
+
const {
|
|
293
|
+
mode, // Current theme setting
|
|
294
|
+
setMode, // Change theme mode
|
|
295
|
+
currentPreset, // Current color preset
|
|
296
|
+
applyPreset, // Apply new preset
|
|
297
|
+
clearPreset // Clear current preset
|
|
298
|
+
} = useTheme()
|
|
299
|
+
|
|
300
|
+
return (
|
|
301
|
+
<div>
|
|
302
|
+
<p>Mode: {mode}</p>
|
|
303
|
+
<p>Preset: {currentPreset?.presetName || 'Default'}</p>
|
|
304
|
+
</div>
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## 🎨 TweakCN Preset Collection
|
|
310
|
+
|
|
311
|
+
Theme Engine includes a complete collection of TweakCN-compatible presets with automatic built-in integration:
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
import { tweakcnPresets, getPresetById, getPresetIds } from 'theme-engine'
|
|
315
|
+
|
|
316
|
+
// Get all preset IDs
|
|
317
|
+
const allPresetIds = getPresetIds()
|
|
318
|
+
|
|
319
|
+
// Get specific preset
|
|
320
|
+
const twitterPreset = getPresetById('twitter')
|
|
321
|
+
|
|
322
|
+
// Access raw TweakCN data
|
|
323
|
+
const rawPresets = tweakcnPresets
|
|
324
|
+
|
|
325
|
+
// Available presets: "twitter", "modern-minimal", "ocean-blue", etc.
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## 🔧 Customization
|
|
329
|
+
|
|
330
|
+
### Zero-Config TweakCN Integration
|
|
331
|
+
|
|
332
|
+
ThemePresetButtons automatically uses the built-in TweakCN preset collection - no configuration needed:
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
import { ThemePresetButtons } from 'theme-engine'
|
|
336
|
+
|
|
337
|
+
function App() {
|
|
338
|
+
return (
|
|
339
|
+
<div>
|
|
340
|
+
{/* Zero config - automatically uses TweakCN presets */}
|
|
341
|
+
<ThemePresetButtons />
|
|
342
|
+
|
|
343
|
+
{/* Filter specific presets if needed */}
|
|
344
|
+
<ThemePresetButtons
|
|
345
|
+
categories={["blue", "nature"]}
|
|
346
|
+
maxPresets={10}
|
|
347
|
+
/>
|
|
348
|
+
</div>
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Custom Preset Integration
|
|
354
|
+
|
|
355
|
+
Add your own presets to the TweakCN collection:
|
|
356
|
+
|
|
357
|
+
```tsx
|
|
358
|
+
import { ThemePreset } from 'theme-engine'
|
|
359
|
+
|
|
360
|
+
const myCustomPreset: ThemePreset = {
|
|
361
|
+
id: "my-brand",
|
|
362
|
+
name: "Brand Theme",
|
|
363
|
+
colors: {
|
|
364
|
+
light: {
|
|
365
|
+
primary: "#0066cc",
|
|
366
|
+
secondary: "#f0f0f0",
|
|
367
|
+
accent: "#ff6600",
|
|
368
|
+
background: "#ffffff",
|
|
369
|
+
foreground: "#1a1a1a",
|
|
370
|
+
// ... all 36+ CSS properties
|
|
371
|
+
},
|
|
372
|
+
dark: {
|
|
373
|
+
primary: "#3399ff",
|
|
374
|
+
secondary: "#2a2a2a",
|
|
375
|
+
accent: "#ff8833",
|
|
376
|
+
background: "#1a1a1a",
|
|
377
|
+
foreground: "#ffffff",
|
|
378
|
+
// ... all 36+ CSS properties
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Apply programmatically via useTheme hook
|
|
384
|
+
function CustomControls() {
|
|
385
|
+
const { applyPreset } = useTheme()
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<button onClick={() => applyPreset(myCustomPreset)}>
|
|
389
|
+
Apply Brand Theme
|
|
390
|
+
</button>
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## 🔄 Migration Guide
|
|
396
|
+
|
|
397
|
+
### Current API Usage
|
|
398
|
+
|
|
399
|
+
Use the clean theming system for all projects:
|
|
400
|
+
```tsx
|
|
401
|
+
import { ThemeProvider, useTheme, ThemeScript } from 'theme-engine'
|
|
402
|
+
|
|
403
|
+
function App() {
|
|
404
|
+
return (
|
|
405
|
+
<html>
|
|
406
|
+
<head>
|
|
407
|
+
<ThemeScript presetStorageKey="app-preset" />
|
|
408
|
+
</head>
|
|
409
|
+
<body>
|
|
410
|
+
<ThemeProvider
|
|
411
|
+
defaultMode="system"
|
|
412
|
+
defaultPreset="twitter"
|
|
413
|
+
enablePresets={true}
|
|
414
|
+
>
|
|
415
|
+
{children}
|
|
416
|
+
</ThemeProvider>
|
|
417
|
+
</body>
|
|
418
|
+
</html>
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function MyComponent() {
|
|
423
|
+
const { applyPreset } = useTheme()
|
|
424
|
+
// Clean and simple!
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Benefits of Clean API
|
|
429
|
+
|
|
430
|
+
- ✅ **Zero configuration** - Works out of the box with sensible defaults
|
|
431
|
+
- ✅ **Perfect coordination** - Dark/light mode works seamlessly with presets
|
|
432
|
+
- ✅ **Optimal performance** - Eliminates theme conflicts and infinite loops
|
|
433
|
+
- ✅ **Reliable persistence** - Separate localStorage keys prevent conflicts
|
|
434
|
+
|
|
435
|
+
## 🎨 CSS Utilities & Modular Styles
|
|
436
|
+
|
|
437
|
+
Theme engine provides modular CSS imports for advanced usage:
|
|
438
|
+
|
|
439
|
+
```css
|
|
440
|
+
/* Complete theme system (recommended) */
|
|
441
|
+
@import "theme-engine/styles";
|
|
442
|
+
|
|
443
|
+
/* Or import specific modules */
|
|
444
|
+
@import "theme-engine/styles/base.css"; /* CSS variables & defaults */
|
|
445
|
+
@import "theme-engine/styles/animations.css"; /* View transition effects */
|
|
446
|
+
@import "theme-engine/styles/components.css"; /* Component styles */
|
|
447
|
+
@import "theme-engine/styles/utilities.css"; /* Dynamic utilities */
|
|
448
|
+
@import "theme-engine/styles/tailwind.css"; /* Complete Tailwind integration */
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Complete CSS Property System
|
|
452
|
+
|
|
453
|
+
Theme engine manages 36+ CSS custom properties across categories:
|
|
454
|
+
|
|
455
|
+
**🎨 Colors (28 properties)**
|
|
456
|
+
- Base: `background`, `foreground`, `card`, `popover`
|
|
457
|
+
- Brand: `primary`, `secondary`, `accent`, `muted`
|
|
458
|
+
- System: `destructive`, `border`, `input`, `ring`
|
|
459
|
+
- Charts: `chart-1` through `chart-5`
|
|
460
|
+
- Sidebar: `sidebar`, `sidebar-primary`, `sidebar-accent`
|
|
461
|
+
|
|
462
|
+
**✍️ Typography (3 properties)**
|
|
463
|
+
- `font-sans`, `font-serif`, `font-mono`
|
|
464
|
+
|
|
465
|
+
**📐 Layout (1 property)**
|
|
466
|
+
- `radius` - Border radius system
|
|
467
|
+
|
|
468
|
+
**🌑 Shadows (6 properties)**
|
|
469
|
+
- `shadow-color`, `shadow-opacity`, `shadow-blur`
|
|
470
|
+
- `shadow-spread`, `shadow-offset-x`, `shadow-offset-y`
|
|
471
|
+
|
|
472
|
+
**📏 Spacing (2 properties)**
|
|
473
|
+
- `letter-spacing`, `spacing`
|
|
474
|
+
|
|
475
|
+
### Dynamic Utilities
|
|
476
|
+
|
|
477
|
+
CSS utilities that adapt to theme variables:
|
|
478
|
+
|
|
479
|
+
```css
|
|
480
|
+
/* Dynamic border radius */
|
|
481
|
+
.rounded-theme { border-radius: var(--radius); }
|
|
482
|
+
.rounded-theme-sm { border-radius: calc(var(--radius) * 0.5); }
|
|
483
|
+
.rounded-theme-lg { border-radius: calc(var(--radius) * 2); }
|
|
484
|
+
|
|
485
|
+
/* Dynamic shadows */
|
|
486
|
+
.shadow-theme {
|
|
487
|
+
box-shadow: 0px var(--shadow-offset-y, 4px) var(--shadow-blur, 8px) 0px
|
|
488
|
+
rgba(0,0,0,var(--shadow-opacity, 0.1));
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/* Dynamic spacing */
|
|
492
|
+
.tracking-theme { letter-spacing: var(--letter-spacing); }
|
|
493
|
+
.gap-theme { gap: var(--spacing, 0.25rem); }
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
## 🤔 Troubleshooting
|
|
497
|
+
|
|
498
|
+
### Theme not persisting on refresh
|
|
499
|
+
|
|
500
|
+
Make sure you're using `ThemeScript` in your `<head>`:
|
|
501
|
+
|
|
502
|
+
```tsx
|
|
503
|
+
<head>
|
|
504
|
+
<ThemeScript presetStorageKey="your-key" />
|
|
505
|
+
</head>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
### Preset colors not applying
|
|
509
|
+
|
|
510
|
+
Ensure you're using `ThemeProvider`:
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
<ThemeProvider enablePresets={true}>
|
|
514
|
+
{children}
|
|
515
|
+
</ThemeProvider>
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### SSR hydration mismatch
|
|
519
|
+
|
|
520
|
+
Add `suppressHydrationWarning` to your `<html>` tag:
|
|
521
|
+
|
|
522
|
+
```tsx
|
|
523
|
+
<html lang="en" suppressHydrationWarning>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
## 📦 Package Contents
|
|
527
|
+
|
|
528
|
+
The npm package includes:
|
|
529
|
+
|
|
530
|
+
- **ESM and CommonJS builds** - Universal compatibility
|
|
531
|
+
- **TypeScript declarations** - Full type safety
|
|
532
|
+
- **Complete CSS system** - All styles included
|
|
533
|
+
- **Source maps** - Enhanced debugging
|
|
534
|
+
- **Tree-shaking support** - Optimal bundle size
|
|
535
|
+
|
|
536
|
+
```
|
|
537
|
+
theme-engine/
|
|
538
|
+
├── dist/
|
|
539
|
+
│ ├── index.js # CommonJS build
|
|
540
|
+
│ ├── index.mjs # ESM build
|
|
541
|
+
│ ├── index.d.ts # TypeScript declarations
|
|
542
|
+
│ └── styles/ # CSS files
|
|
543
|
+
│ ├── index.css # Complete system
|
|
544
|
+
│ ├── base.css # CSS variables
|
|
545
|
+
│ ├── animations.css
|
|
546
|
+
│ ├── components.css
|
|
547
|
+
│ ├── utilities.css
|
|
548
|
+
│ └── tailwind.css
|
|
549
|
+
└── README.md
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
## 🏗️ Architecture
|
|
553
|
+
|
|
554
|
+
The theme engine elegantly coordinates two theming concerns:
|
|
555
|
+
|
|
556
|
+
- **Theme Mode** (light/dark/system) - Controlled by `ThemeProvider`
|
|
557
|
+
- **Color Presets** - Managed by `ThemeProvider` using CSS variables with `!important`
|
|
558
|
+
- **Coordination** - MutationObserver detects mode changes and reapplies presets
|
|
559
|
+
- **Persistence** - Separate localStorage keys prevent conflicts
|
|
560
|
+
- **Zero-Config** - Built-in TweakCN presets work automatically
|
|
561
|
+
|
|
562
|
+
This architecture ensures seamless coordination between appearance modes and color presets without conflicts.
|
|
563
|
+
|
|
564
|
+
## 📄 License
|
|
565
|
+
|
|
566
|
+
MIT License - see the monorepo root for details.
|