@fakhrirafiki/theme-engine 0.4.19 → 0.4.20

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.
Files changed (2) hide show
  1. package/README.md +110 -122
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # 🎨 Theme Engine
1
+ # 🎨 useThemeEngine for **Next.js (App Router)**
2
2
 
3
- Theme system for **Next.js (App Router)**: mode (`light | dark | system`) + theme presets (semantic tokens via CSS variables).
3
+ Dark mode + theme presets (semantic tokens via CSS variables).
4
4
 
5
5
  > ✅ Opinionated defaults, minimal setup, and TypeScript autocomplete that “just works”.
6
6
 
@@ -8,15 +8,15 @@ Theme system for **Next.js (App Router)**: mode (`light | dark | system`) + them
8
8
  ![npm downloads](https://img.shields.io/npm/dm/@fakhrirafiki/theme-engine)
9
9
  ![license](https://img.shields.io/npm/l/@fakhrirafiki/theme-engine)
10
10
 
11
- Live demo: https://theme-engine-example.vercel.app/
12
- Example repo: https://github.com/fakhrirafiki/theme-engine-example
11
+ - Live demo: https://theme-engine-example.vercel.app/
12
+ - Example repo: https://github.com/fakhrirafiki/theme-engine-example
13
13
 
14
14
  ## ✨ Why use this?
15
15
 
16
+ - 🧠 **DX-first**: `useThemeEngine()` for everything
16
17
  - ⚡ **Fast setup**: 1 CSS import + 1 provider
17
18
  - 🌓 **Mode support**: `light | dark | system` (with View Transition ripple when supported)
18
19
  - 🎨 **Theme presets**: built-in presets + your own presets
19
- - 🧠 **DX-first**: `useThemeEngine()` for everything
20
20
  - 🧩 **Tailwind v4 friendly**: `@theme inline` tokens included (works with shadcn-style semantic tokens)
21
21
 
22
22
  ## 📚 Table of contents
@@ -50,14 +50,14 @@ pnpm add @fakhrirafiki/theme-engine
50
50
  In `src/app/globals.css`:
51
51
 
52
52
  ```css
53
- @import '@fakhrirafiki/theme-engine/styles';
53
+ @import "@fakhrirafiki/theme-engine/styles";
54
54
  ```
55
55
 
56
56
  ✅ Tailwind v4 (recommended order):
57
57
 
58
58
  ```css
59
- @import 'tailwindcss';
60
- @import '@fakhrirafiki/theme-engine/styles';
59
+ @import "tailwindcss";
60
+ @import "@fakhrirafiki/theme-engine/styles";
61
61
 
62
62
  @custom-variant dark (&:is(.dark *));
63
63
  ```
@@ -65,10 +65,10 @@ In `src/app/globals.css`:
65
65
  ℹ️ Not using Tailwind v4?
66
66
 
67
67
  ```css
68
- @import '@fakhrirafiki/theme-engine/styles/base.css';
69
- @import '@fakhrirafiki/theme-engine/styles/animations.css';
70
- @import '@fakhrirafiki/theme-engine/styles/components.css';
71
- @import '@fakhrirafiki/theme-engine/styles/utilities.css';
68
+ @import "@fakhrirafiki/theme-engine/styles/base.css";
69
+ @import "@fakhrirafiki/theme-engine/styles/animations.css";
70
+ @import "@fakhrirafiki/theme-engine/styles/components.css";
71
+ @import "@fakhrirafiki/theme-engine/styles/utilities.css";
72
72
  ```
73
73
 
74
74
  ### 2) Wrap your app with `ThemeProvider`
@@ -76,9 +76,9 @@ In `src/app/globals.css`:
76
76
  In `src/app/layout.tsx`:
77
77
 
78
78
  ```tsx
79
- import type { ReactNode } from 'react';
80
- import { ThemeProvider } from '@fakhrirafiki/theme-engine';
81
- import './globals.css';
79
+ import type { ReactNode } from "react";
80
+ import { ThemeProvider } from "@fakhrirafiki/theme-engine";
81
+ import "./globals.css";
82
82
 
83
83
  export default function RootLayout({ children }: { children: ReactNode }) {
84
84
  return (
@@ -100,18 +100,18 @@ export default function RootLayout({ children }: { children: ReactNode }) {
100
100
  Toggle mode:
101
101
 
102
102
  ```tsx
103
- 'use client';
103
+ "use client";
104
104
 
105
- import { useThemeEngine } from '@fakhrirafiki/theme-engine';
105
+ import { useThemeEngine } from "@fakhrirafiki/theme-engine";
106
106
 
107
107
  export function ModeButtons() {
108
108
  const { mode, setDarkMode, toggleDarkMode } = useThemeEngine();
109
109
 
110
110
  return (
111
111
  <div>
112
- <button onClick={() => setDarkMode('system')}>System</button>
113
- <button onClick={() => setDarkMode('light')}>Light</button>
114
- <button onClick={() => setDarkMode('dark')}>Dark</button>
112
+ <button onClick={() => setDarkMode("system")}>System</button>
113
+ <button onClick={() => setDarkMode("light")}>Light</button>
114
+ <button onClick={() => setDarkMode("dark")}>Dark</button>
115
115
  <button onClick={() => toggleDarkMode()}>Toggle</button>
116
116
  <div>Current: {mode}</div>
117
117
  </div>
@@ -122,18 +122,18 @@ export function ModeButtons() {
122
122
  Pick a theme preset by ID:
123
123
 
124
124
  ```tsx
125
- 'use client';
125
+ "use client";
126
126
 
127
- import { useThemeEngine } from '@fakhrirafiki/theme-engine';
127
+ import { useThemeEngine } from "@fakhrirafiki/theme-engine";
128
128
 
129
129
  export function PresetButtons() {
130
130
  const { applyThemeById, clearTheme, currentTheme } = useThemeEngine();
131
131
 
132
132
  return (
133
133
  <div>
134
- <button onClick={() => applyThemeById('modern-minimal')}>Modern Minimal</button>
134
+ <button onClick={() => applyThemeById("modern-minimal")}>Modern Minimal</button>
135
135
  <button onClick={() => clearTheme()}>Reset</button>
136
- <div>Active: {currentTheme?.presetName ?? 'Default'}</div>
136
+ <div>Active: {currentTheme?.presetName ?? "Default"}</div>
137
137
  </div>
138
138
  );
139
139
  }
@@ -142,18 +142,18 @@ export function PresetButtons() {
142
142
  💡 Want typed autocomplete (built-in IDs + your custom IDs)? Use a generic:
143
143
 
144
144
  ```tsx
145
- 'use client';
145
+ "use client";
146
146
 
147
- import { ThemePresets, useThemeEngine } from '@fakhrirafiki/theme-engine';
148
- import { customPresets } from './custom-theme-presets';
147
+ import { ThemePresets, useThemeEngine } from "@fakhrirafiki/theme-engine";
148
+ import { customPresets } from "./custom-theme-presets";
149
149
 
150
150
  export function TypedPresetButtons() {
151
151
  const { applyThemeById } = useThemeEngine<ThemePresets<typeof customPresets>>();
152
152
 
153
153
  return (
154
154
  <div>
155
- <button onClick={() => applyThemeById('my-brand')}>My Brand</button>
156
- <button onClick={() => applyThemeById('modern-minimal')}>Modern Minimal</button>
155
+ <button onClick={() => applyThemeById("my-brand")}>My Brand</button>
156
+ <button onClick={() => applyThemeById("modern-minimal")}>Modern Minimal</button>
157
157
  </div>
158
158
  );
159
159
  }
@@ -191,13 +191,7 @@ If you run multiple apps on the same domain, override the keys:
191
191
 
192
192
  ---
193
193
 
194
- ## 🧩 Custom presets (recommended)
195
-
196
- Create presets in TweakCN-compatible format and pass them into `ThemeProvider`.
197
-
198
- ✅ Tip: use `satisfies` to preserve literal keys for TS autocomplete:
199
-
200
- ### 🎛️ Get a brand theme from TweakCN (recommended)
194
+ ## 🧩 Get your brand theme from TweakCN (recommended)
201
195
 
202
196
  The fastest way to create a great-looking preset is to use the TweakCN editor:
203
197
 
@@ -206,31 +200,31 @@ The fastest way to create a great-looking preset is to use the TweakCN editor:
206
200
  Pick a theme, tweak the colors, then copy the preset output and paste it into your `customPresets` object (it matches the `TweakCNThemePreset` shape).
207
201
 
208
202
  ```ts
209
- import { type TweakCNThemePreset } from '@fakhrirafiki/theme-engine';
203
+ import { type TweakCNThemePreset } from "@fakhrirafiki/theme-engine";
210
204
 
211
205
  export const customPresets = {
212
- 'my-brand': {
213
- label: 'My Brand',
206
+ "my-brand": {
207
+ label: "My Brand",
214
208
  styles: {
215
209
  light: {
216
- background: '#ffffff',
217
- foreground: '#111827',
218
- primary: '#2563eb',
219
- 'primary-foreground': '#ffffff',
220
- secondary: '#e5e7eb',
221
- 'secondary-foreground': '#111827',
222
- card: '#ffffff',
223
- 'card-foreground': '#111827',
210
+ background: "#ffffff",
211
+ foreground: "#111827",
212
+ primary: "#2563eb",
213
+ "primary-foreground": "#ffffff",
214
+ secondary: "#e5e7eb",
215
+ "secondary-foreground": "#111827",
216
+ card: "#ffffff",
217
+ "card-foreground": "#111827",
224
218
  },
225
219
  dark: {
226
- background: '#0b1020',
227
- foreground: '#f9fafb',
228
- primary: '#60a5fa',
229
- 'primary-foreground': '#0b1020',
230
- secondary: '#1f2937',
231
- 'secondary-foreground': '#f9fafb',
232
- card: '#111827',
233
- 'card-foreground': '#f9fafb',
220
+ background: "#0b1020",
221
+ foreground: "#f9fafb",
222
+ primary: "#60a5fa",
223
+ "primary-foreground": "#0b1020",
224
+ secondary: "#1f2937",
225
+ "secondary-foreground": "#f9fafb",
226
+ card: "#111827",
227
+ "card-foreground": "#f9fafb",
234
228
  },
235
229
  },
236
230
  },
@@ -240,9 +234,9 @@ export const customPresets = {
240
234
  Then in your providers/layout:
241
235
 
242
236
  ```tsx
243
- import type { ReactNode } from 'react';
244
- import { ThemeProvider } from '@fakhrirafiki/theme-engine';
245
- import { customPresets } from './custom-theme-presets';
237
+ import type { ReactNode } from "react";
238
+ import { ThemeProvider } from "@fakhrirafiki/theme-engine";
239
+ import { customPresets } from "./custom-theme-presets";
246
240
 
247
241
  export function AppProviders({ children }: { children: ReactNode }) {
248
242
  return (
@@ -266,10 +260,10 @@ Notes:
266
260
  The package ships with a built-in preset collection:
267
261
 
268
262
  ```ts
269
- import { getPresetIds, getPresetById } from '@fakhrirafiki/theme-engine';
263
+ import { getPresetIds, getPresetById } from "@fakhrirafiki/theme-engine";
270
264
 
271
265
  const ids = getPresetIds();
272
- const modernMinimal = getPresetById('modern-minimal');
266
+ const modernMinimal = getPresetById("modern-minimal");
273
267
  ```
274
268
 
275
269
  ---
@@ -278,24 +272,24 @@ const modernMinimal = getPresetById('modern-minimal');
278
272
 
279
273
  After importing `@fakhrirafiki/theme-engine/styles`, you can use semantic tokens like:
280
274
 
281
- | Category | Tailwind class examples | Backed by preset CSS variables | Notes |
282
- | --- | --- | --- | --- |
283
- | Surfaces | `bg-background`, `text-foreground` | `--background`, `--foreground` | Base app background + text |
284
- | Cards | `bg-card`, `text-card-foreground` | `--card`, `--card-foreground` | Cards / panels |
285
- | Popovers | `bg-popover`, `text-popover-foreground` | `--popover`, `--popover-foreground` | Popovers / dropdowns |
286
- | Brand / actions | `bg-primary`, `text-primary-foreground` | `--primary`, `--primary-foreground` | Primary buttons / highlights |
287
- | Secondary | `bg-secondary`, `text-secondary-foreground` | `--secondary`, `--secondary-foreground` | Secondary UI surfaces |
288
- | Muted | `bg-muted`, `text-muted-foreground` | `--muted`, `--muted-foreground` | Subtle backgrounds / helper text |
289
- | Accent | `bg-accent`, `text-accent-foreground` | `--accent`, `--accent-foreground` | Emphasis (not status colors) |
290
- | Destructive | `bg-destructive`, `text-destructive-foreground` | `--destructive`, `--destructive-foreground` | Danger actions |
291
- | Borders / focus | `border-border`, `border-input`, `ring-ring` | `--border`, `--input`, `--ring` | Used by `outline-ring/50` too |
292
- | Charts | `bg-chart-1`, `text-chart-2` | `--chart-1` ... `--chart-5` | Data viz palettes |
293
- | Sidebar | `bg-sidebar`, `text-sidebar-foreground`, `bg-sidebar-primary`, `border-sidebar-border` | `--sidebar-*` | Handy for dashboard layouts |
294
- | Status accents | `bg-accent-success`, `text-accent-danger-foreground` | `--accent-<name>`, `--accent-<name>-foreground` | Optional: only if preset defines `accent-*` |
295
- | Radius scale | `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl` | `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl` | Derived from `--radius` |
296
- | Tracking scale | `tracking-tighter`, `tracking-wide` | `--tracking-*` | Derived from `--letter-spacing` |
297
- | Fonts | `font-sans`, `font-serif`, `font-mono` | `--font-sans`, `--font-serif`, `--font-mono` | Defaults in `base.css` |
298
- | Shadows | `shadow-sm`, `shadow-md`, `shadow-xl` | `--shadow-*` | Derived from `--shadow-*` knobs |
275
+ | Category | Tailwind class examples | Backed by preset CSS variables | Notes |
276
+ | --------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------- |
277
+ | Surfaces | `bg-background`, `text-foreground` | `--background`, `--foreground` | Base app background + text |
278
+ | Cards | `bg-card`, `text-card-foreground` | `--card`, `--card-foreground` | Cards / panels |
279
+ | Popovers | `bg-popover`, `text-popover-foreground` | `--popover`, `--popover-foreground` | Popovers / dropdowns |
280
+ | Brand / actions | `bg-primary`, `text-primary-foreground` | `--primary`, `--primary-foreground` | Primary buttons / highlights |
281
+ | Secondary | `bg-secondary`, `text-secondary-foreground` | `--secondary`, `--secondary-foreground` | Secondary UI surfaces |
282
+ | Muted | `bg-muted`, `text-muted-foreground` | `--muted`, `--muted-foreground` | Subtle backgrounds / helper text |
283
+ | Accent | `bg-accent`, `text-accent-foreground` | `--accent`, `--accent-foreground` | Emphasis (not status colors) |
284
+ | Destructive | `bg-destructive`, `text-destructive-foreground` | `--destructive`, `--destructive-foreground` | Danger actions |
285
+ | Borders / focus | `border-border`, `border-input`, `ring-ring` | `--border`, `--input`, `--ring` | Used by `outline-ring/50` too |
286
+ | Charts | `bg-chart-1`, `text-chart-2` | `--chart-1` ... `--chart-5` | Data viz palettes |
287
+ | Sidebar | `bg-sidebar`, `text-sidebar-foreground`, `bg-sidebar-primary`, `border-sidebar-border` | `--sidebar-*` | Handy for dashboard layouts |
288
+ | Status accents | `bg-accent-success`, `text-accent-danger-foreground` | `--accent-<name>`, `--accent-<name>-foreground` | Optional: only if preset defines `accent-*` |
289
+ | Radius scale | `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-xl` | `--radius-sm`, `--radius-md`, `--radius-lg`, `--radius-xl` | Derived from `--radius` |
290
+ | Tracking scale | `tracking-tighter`, `tracking-wide` | `--tracking-*` | Derived from `--letter-spacing` |
291
+ | Fonts | `font-sans`, `font-serif`, `font-mono` | `--font-sans`, `--font-serif`, `--font-mono` | Defaults in `base.css` |
292
+ | Shadows | `shadow-sm`, `shadow-md`, `shadow-xl` | `--shadow-*` | Derived from `--shadow-*` knobs |
299
293
 
300
294
  ---
301
295
 
@@ -306,9 +300,9 @@ After importing `@fakhrirafiki/theme-engine/styles`, you can use semantic tokens
306
300
  Ready-made mode toggle button (with View Transition ripple when supported).
307
301
 
308
302
  ```tsx
309
- 'use client';
303
+ "use client";
310
304
 
311
- import { ThemeToggle } from '@fakhrirafiki/theme-engine';
305
+ import { ThemeToggle } from "@fakhrirafiki/theme-engine";
312
306
 
313
307
  export function HeaderThemeToggle() {
314
308
  return <ThemeToggle size="md" variant="ghost" />;
@@ -320,9 +314,9 @@ export function HeaderThemeToggle() {
320
314
  Animated preset picker (shows custom presets first, then built-ins):
321
315
 
322
316
  ```tsx
323
- 'use client';
317
+ "use client";
324
318
 
325
- import { ThemePresetButtons } from '@fakhrirafiki/theme-engine';
319
+ import { ThemePresetButtons } from "@fakhrirafiki/theme-engine";
326
320
 
327
321
  export function PresetPicker() {
328
322
  return <ThemePresetButtons />;
@@ -336,21 +330,16 @@ Want a simple, scrollable preset list (e.g. for a settings modal)? Copy-paste th
336
330
  > Note: this snippet uses Tailwind utility classes. If you don’t use Tailwind, replace the classes with your own styles/UI components.
337
331
 
338
332
  ```tsx
339
- 'use client';
333
+ "use client";
340
334
 
341
- import { formatColor, useThemeEngine } from '@fakhrirafiki/theme-engine';
335
+ import { formatColor, useThemeEngine } from "@fakhrirafiki/theme-engine";
342
336
 
343
337
  type ThemePresetSelectProps = {
344
338
  allowedPresetIds?: string[];
345
339
  };
346
340
 
347
341
  export function ThemePresetSelect({
348
- allowedPresetIds = [
349
- 'modern-minimal',
350
- 'violet-bloom',
351
- 'vercel',
352
- 'mono',
353
- ],
342
+ allowedPresetIds = ["modern-minimal", "violet-bloom", "vercel", "mono"],
354
343
  }: ThemePresetSelectProps) {
355
344
  const { currentTheme, applyThemeById, availablePresets, resolvedMode } = useThemeEngine();
356
345
 
@@ -366,7 +355,7 @@ export function ThemePresetSelect({
366
355
  const preset = availablePresets[presetId];
367
356
  if (!preset) return [];
368
357
 
369
- const scheme = resolvedMode === 'dark' ? preset.styles.dark : preset.styles.light;
358
+ const scheme = resolvedMode === "dark" ? preset.styles.dark : preset.styles.light;
370
359
  const primary = (scheme as any).primary as string | undefined;
371
360
  const secondary = (scheme as any).secondary as string | undefined;
372
361
  const accent = (scheme as any).accent as string | undefined;
@@ -386,8 +375,8 @@ export function ThemePresetSelect({
386
375
  type="button"
387
376
  className={`w-full rounded-full border px-3 py-2 text-xs transition-colors ${
388
377
  isActive
389
- ? 'border-primary/70 bg-primary/10 text-foreground'
390
- : 'border-border bg-muted/40 text-muted-foreground hover:border-muted-foreground/40 hover:bg-muted/60'
378
+ ? "border-primary/70 bg-primary/10 text-foreground"
379
+ : "border-border bg-muted/40 text-muted-foreground hover:border-muted-foreground/40 hover:bg-muted/60"
391
380
  }`}
392
381
  onClick={() => applyThemeById(preset.id)}
393
382
  >
@@ -399,7 +388,7 @@ export function ThemePresetSelect({
399
388
  <span
400
389
  key={index}
401
390
  className="inline-block h-2.5 w-2.5 rounded-full border border-foreground/10 shadow-sm"
402
- style={{ backgroundColor: formatColor(color, 'hex') }}
391
+ style={{ backgroundColor: formatColor(color, "hex") }}
403
392
  />
404
393
  ))}
405
394
  </span>
@@ -440,15 +429,14 @@ export function ThemePresetSelect({
440
429
  />
441
430
  ```
442
431
 
443
- | Prop | Type | Default | Description |
444
- | --- | --- | --- | --- |
445
- | `children` | `ReactNode` | required | React subtree |
446
- | `defaultMode` | `Mode` | `'system'` | Used when no persisted value |
447
- | `defaultPreset` | `BuiltInPresetId \| keyof customPresets` | `undefined` | Default preset (see SSR note) |
448
- | `modeStorageKey` | `string` | `'theme-engine-theme'` | `localStorage` key for mode |
449
- | `presetStorageKey` | `string` | `'theme-preset'` | `localStorage` key for preset |
450
- | `customPresets` | `Record<string, TweakCNThemePreset>` | `undefined` | Add your own presets (can override built-ins by ID) |
451
- | `Pre-hydration script` | n/a | always on | `ThemeProvider` always injects a pre-hydration script for preset restoration |
432
+ | Prop | Type | Default | Description |
433
+ | ------------------ | ---------------------------------------- | ---------------------- | --------------------------------------------------- |
434
+ | `children` | `ReactNode` | required | React subtree |
435
+ | `defaultMode` | `Mode` | `'system'` | Used when no persisted value for dark mode |
436
+ | `defaultPreset` | `BuiltInPresetId \| keyof customPresets` | `undefined` | Default preset (see SSR note) |
437
+ | `modeStorageKey` | `string` | `'theme-engine-theme'` | `localStorage` key for mode |
438
+ | `presetStorageKey` | `string` | `'theme-preset'` | `localStorage` key for preset |
439
+ | `customPresets` | `Record<string, TweakCNThemePreset>` | `undefined` | Add your own presets (can override built-ins by ID) |
452
440
 
453
441
  ### `useThemeEngine()`
454
442
 
@@ -461,32 +449,32 @@ useThemeEngine<TCustomPresets = undefined>()
461
449
  To get typed custom preset IDs:
462
450
 
463
451
  ```ts
464
- useThemeEngine<ThemePresets<typeof customPresets>>()
452
+ useThemeEngine<ThemePresets<typeof customPresets>>();
465
453
  ```
466
454
 
467
455
  Return fields:
468
456
 
469
- | Field | Type | Description |
470
- | --- | --- | --- |
471
- | `darkMode` | `boolean` | `resolvedMode === 'dark'` |
472
- | `mode` | `'light' \| 'dark' \| 'system'` | Current user preference |
473
- | `resolvedMode` | `'light' \| 'dark'` | Resolved mode (never `system`) |
474
- | `setDarkMode` | `(mode: Mode) => void` | Set `light \| dark \| system` |
475
- | `toggleDarkMode` | `(coords?: { x: number; y: number }) => void` | Toggles light/dark (and exits `system`) |
476
- | `applyThemeById` | `(id: ThemeId) => void` | Apply a preset by ID (alias: `applyPresetById`) |
477
- | `clearTheme` | `() => void` | Clear preset and fall back to `defaultPreset` if provided (alias: `clearPreset`) |
478
- | `currentTheme` | `{ presetId; presetName; colors; appliedAt } \| null` | Current preset (alias: `currentPreset`) |
479
- | `isUsingDefaultPreset` | `boolean` | Whether current preset equals `defaultPreset` |
480
- | `availablePresets` | `Record<string, TweakCNThemePreset>` | Built-in + custom |
481
- | `builtInPresets` | `Record<string, TweakCNThemePreset>` | Built-in only |
482
- | `customPresets` | `Record<string, TweakCNThemePreset>` | Custom only |
457
+ | Field | Type | Description |
458
+ | ---------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
459
+ | `darkMode` | `boolean` | `resolvedMode === 'dark'` |
460
+ | `mode` | `'light' \| 'dark' \| 'system'` | Current user preference |
461
+ | `resolvedMode` | `'light' \| 'dark'` | Resolved mode (never `system`) |
462
+ | `setDarkMode` | `(mode: Mode) => void` | Set `light \| dark \| system` |
463
+ | `toggleDarkMode` | `(coords?: { x: number; y: number }) => void` | Toggles light/dark (and exits `system`) |
464
+ | `applyThemeById` | `(id: ThemeId) => void` | Apply a preset by ID (alias: `applyPresetById`) |
465
+ | `clearTheme` | `() => void` | Clear preset and fall back to `defaultPreset` if provided (alias: `clearPreset`) |
466
+ | `currentTheme` | `{ presetId; presetName; colors; appliedAt } \| null` | Current preset (alias: `currentPreset`) |
467
+ | `isUsingDefaultPreset` | `boolean` | Whether current preset equals `defaultPreset` |
468
+ | `availablePresets` | `Record<string, TweakCNThemePreset>` | Built-in + custom |
469
+ | `builtInPresets` | `Record<string, TweakCNThemePreset>` | Built-in only |
470
+ | `customPresets` | `Record<string, TweakCNThemePreset>` | Custom only |
483
471
 
484
472
  ### Utilities
485
473
 
486
- | Export | Description |
487
- | --- | --- |
488
- | `formatColor(color, format)` | Converts a color string into `hsl`/`rgb`/`hex` |
489
- | `withAlpha(hslTriplet, alpha)` | Adds alpha to an HSL triplet |
474
+ | Export | Description |
475
+ | ------------------------------ | ---------------------------------------------- |
476
+ | `formatColor(color, format)` | Converts a color string into `hsl`/`rgb`/`hex` |
477
+ | `withAlpha(hslTriplet, alpha)` | Adds alpha to an HSL triplet |
490
478
 
491
479
  ---
492
480
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fakhrirafiki/theme-engine",
3
- "version": "0.4.19",
3
+ "version": "0.4.20",
4
4
  "description": "Elegant theming system with smooth transitions, custom presets, semantic accent colors, and complete shadcn/ui support for modern React applications",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",