@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 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.