@dheme/react 2.11.0 → 2.12.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 CHANGED
@@ -79,6 +79,7 @@ The main provider. Manages theme state, API calls, caching, and CSS variable app
79
79
  saturationAdjust: 10,
80
80
  secondaryColor: '#10b981',
81
81
  borderIsColored: false,
82
+ tailwindVersion: 'v4', // 'v3' | 'v4' (default: 'v4')
82
83
  }}
83
84
  defaultMode="light" // 'light' | 'dark' (default: 'light')
84
85
  persist={true} // Cache in localStorage (default: true)
@@ -103,6 +104,9 @@ The main provider. Manages theme state, API calls, caching, and CSS variable app
103
104
  | `onThemeChange` | `(theme: GenerateThemeResponse) => void` | - | Called when theme data changes. |
104
105
  | `onModeChange` | `(mode: ThemeMode) => void` | - | Called when mode changes. |
105
106
  | `onError` | `(error: Error) => void` | - | Called on API errors. |
107
+ | `fallback` | `React.ReactNode` | - | Shown while the theme is loading for the first time. |
108
+
109
+ > **`themeParams.tailwindVersion`** controls the CSS variable format applied to `:root`. Use `'v3'` for projects that wrap variables with `hsl(var(--token))` (Tailwind v3 / shadcn/ui default), or `'v4'` (default) for projects that use `var(--token)` directly (Tailwind v4 / `@theme inline`).
106
110
 
107
111
  ### `<DhemeScript>`
108
112
 
@@ -259,7 +263,22 @@ The provider also syncs the `dark` class on `<html>` automatically.
259
263
 
260
264
  ## CSS Variables
261
265
 
262
- The provider sets 19 CSS variables + `--radius` on `:root`:
266
+ The provider sets 19 CSS variables + `--radius` on `:root`. The value format depends on `themeParams.tailwindVersion`:
267
+
268
+ **Tailwind v4** (default) — wrapped `hsl()`, use with `var(--token)`:
269
+
270
+ ```css
271
+ :root {
272
+ --background: hsl(0 0% 100%);
273
+ --foreground: hsl(222.2 84% 4.9%);
274
+ --primary: hsl(221.2 83.2% 53.3%);
275
+ --primary-foreground: hsl(210 40% 98%);
276
+ /* ... 15 more tokens */
277
+ --radius: 0.5rem;
278
+ }
279
+ ```
280
+
281
+ **Tailwind v3** (`tailwindVersion: 'v3'`) — bare channels, use with `hsl(var(--token))`:
263
282
 
264
283
  ```css
265
284
  :root {
@@ -272,8 +291,6 @@ The provider sets 19 CSS variables + `--radius` on `:root`:
272
291
  }
273
292
  ```
274
293
 
275
- Values are in shadcn/ui format (`h s% l%`), directly compatible with Tailwind CSS `hsl()` usage.
276
-
277
294
  ## ThemeGenerator
278
295
 
279
296
  A floating FAB (Floating Action Button) that lets users generate and preview themes in real time — directly inside your app. No external dependencies beyond React itself.
@@ -409,25 +426,31 @@ const [open, setOpen] = useState(false);
409
426
 
410
427
  ## Utilities
411
428
 
412
- ### `themeToCSS(theme, mode)`
429
+ ### `themeToCSS(theme, mode, tailwindVersion?)`
413
430
 
414
431
  Convert a `GenerateThemeResponse` to a CSS variable assignment string.
415
432
 
416
433
  ```typescript
417
434
  import { themeToCSS } from '@dheme/react';
418
435
 
436
+ // Tailwind v4 (default) — hsl() wrapped
419
437
  const css = themeToCSS(theme, 'light');
438
+ // "--background:hsl(0 0% 100%);--foreground:hsl(222.2 84% 4.9%);..."
439
+
440
+ // Tailwind v3 — bare channels
441
+ const css = themeToCSS(theme, 'light', 'v3');
420
442
  // "--background:0 0% 100%;--foreground:222.2 84% 4.9%;..."
421
443
  ```
422
444
 
423
- ### `applyThemeCSSVariables(theme, mode)`
445
+ ### `applyThemeCSSVariables(theme, mode, tailwindVersion?)`
424
446
 
425
447
  Manually apply CSS variables to `:root`.
426
448
 
427
449
  ```typescript
428
450
  import { applyThemeCSSVariables } from '@dheme/react';
429
451
 
430
- applyThemeCSSVariables(theme, 'dark');
452
+ applyThemeCSSVariables(theme, 'dark'); // Tailwind v4 (default)
453
+ applyThemeCSSVariables(theme, 'dark', 'v3'); // Tailwind v3
431
454
  ```
432
455
 
433
456
  ### `removeThemeCSSVariables()`
package/dist/index.d.mts CHANGED
@@ -107,9 +107,9 @@ declare function useGenerateTheme(): {
107
107
 
108
108
  declare function useDhemeClient(): DhemeClient;
109
109
 
110
- declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode): string;
111
- declare function themeToCSSBothModes(theme: GenerateThemeResponse): string;
112
- declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode): void;
110
+ declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): string;
111
+ declare function themeToCSSBothModes(theme: GenerateThemeResponse, tailwindVersion?: 'v3' | 'v4'): string;
112
+ declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): void;
113
113
  declare function removeThemeCSSVariables(): void;
114
114
 
115
115
  declare function buildCacheKey(params: GenerateThemeRequest): string;
package/dist/index.d.ts CHANGED
@@ -107,9 +107,9 @@ declare function useGenerateTheme(): {
107
107
 
108
108
  declare function useDhemeClient(): DhemeClient;
109
109
 
110
- declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode): string;
111
- declare function themeToCSSBothModes(theme: GenerateThemeResponse): string;
112
- declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode): void;
110
+ declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): string;
111
+ declare function themeToCSSBothModes(theme: GenerateThemeResponse, tailwindVersion?: 'v3' | 'v4'): string;
112
+ declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): void;
113
113
  declare function removeThemeCSSVariables(): void;
114
114
 
115
115
  declare function buildCacheKey(params: GenerateThemeRequest): string;
package/dist/index.js CHANGED
@@ -110,17 +110,18 @@ var TOKEN_TO_CSS_VAR = {
110
110
  };
111
111
 
112
112
  // src/utils/cssVariables.ts
113
- function formatHSL(color) {
114
- return `hsl(${color.h} ${color.s}% ${color.l}%)`;
113
+ function formatHSL(color, tailwindVersion = "v4") {
114
+ const bare = `${color.h} ${color.s}% ${color.l}%`;
115
+ return tailwindVersion === "v3" ? bare : `hsl(${bare})`;
115
116
  }
116
- function themeToCSS(theme, mode) {
117
+ function themeToCSS(theme, mode, tailwindVersion = "v4") {
117
118
  const colors = theme.colors[mode];
118
119
  if (!colors) return "";
119
120
  const parts = [];
120
121
  for (const key of CSS_TOKEN_KEYS) {
121
122
  const color = colors[key];
122
123
  if (color) {
123
- parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color)}`);
124
+ parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color, tailwindVersion)}`);
124
125
  }
125
126
  }
126
127
  if (theme.radius != null) {
@@ -128,12 +129,12 @@ function themeToCSS(theme, mode) {
128
129
  }
129
130
  return parts.join(";");
130
131
  }
131
- function themeToCSSBothModes(theme) {
132
- const lightCSS = themeToCSS(theme, "light");
133
- const darkCSS = themeToCSS(theme, "dark");
132
+ function themeToCSSBothModes(theme, tailwindVersion = "v4") {
133
+ const lightCSS = themeToCSS(theme, "light", tailwindVersion);
134
+ const darkCSS = themeToCSS(theme, "dark", tailwindVersion);
134
135
  return `:root{${lightCSS}}.dark{${darkCSS}}`;
135
136
  }
136
- function applyThemeCSSVariables(theme, mode) {
137
+ function applyThemeCSSVariables(theme, mode, tailwindVersion = "v4") {
137
138
  if (typeof document === "undefined") return;
138
139
  const colors = theme.colors[mode];
139
140
  if (!colors) return;
@@ -141,7 +142,7 @@ function applyThemeCSSVariables(theme, mode) {
141
142
  for (const key of CSS_TOKEN_KEYS) {
142
143
  const color = colors[key];
143
144
  if (color) {
144
- style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color));
145
+ style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color, tailwindVersion));
145
146
  }
146
147
  }
147
148
  if (theme.radius != null) {
@@ -297,7 +298,7 @@ function DhemeProvider({
297
298
  }, []);
298
299
  (0, import_react3.useEffect)(() => {
299
300
  if (theme && autoApply) {
300
- applyThemeCSSVariables(theme, mode);
301
+ applyThemeCSSVariables(theme, mode, themeParams?.tailwindVersion ?? "v4");
301
302
  }
302
303
  if (typeof document !== "undefined") {
303
304
  if (mode === "dark") {
@@ -335,7 +336,7 @@ function DhemeProvider({
335
336
  setTheme(data);
336
337
  setIsReady(true);
337
338
  if (autoApplyRef.current) {
338
- applyThemeCSSVariables(data, modeRef.current);
339
+ applyThemeCSSVariables(data, modeRef.current, params.tailwindVersion ?? "v4");
339
340
  }
340
341
  if (persistRef.current) {
341
342
  const key = buildCacheKey(params);
@@ -374,7 +375,7 @@ function DhemeProvider({
374
375
  if (cached) {
375
376
  setTheme(cached);
376
377
  setIsReady(true);
377
- if (autoApply) applyThemeCSSVariables(cached, mode);
378
+ if (autoApply) applyThemeCSSVariables(cached, mode, params.tailwindVersion ?? "v4");
378
379
  const controller = new AbortController();
379
380
  abortRef.current = controller;
380
381
  fetchTheme(params).then((data) => {
@@ -383,7 +384,8 @@ function DhemeProvider({
383
384
  const freshLight = JSON.stringify(data.colors.light);
384
385
  if (cachedLight !== freshLight) {
385
386
  setTheme(data);
386
- if (autoApply) applyThemeCSSVariables(data, modeRef.current);
387
+ if (autoApply)
388
+ applyThemeCSSVariables(data, modeRef.current, params.tailwindVersion ?? "v4");
387
389
  saveThemeToCache(cacheKey, data);
388
390
  onThemeChangeRef.current?.(data);
389
391
  }
package/dist/index.mjs CHANGED
@@ -60,17 +60,18 @@ var TOKEN_TO_CSS_VAR = {
60
60
  };
61
61
 
62
62
  // src/utils/cssVariables.ts
63
- function formatHSL(color) {
64
- return `hsl(${color.h} ${color.s}% ${color.l}%)`;
63
+ function formatHSL(color, tailwindVersion = "v4") {
64
+ const bare = `${color.h} ${color.s}% ${color.l}%`;
65
+ return tailwindVersion === "v3" ? bare : `hsl(${bare})`;
65
66
  }
66
- function themeToCSS(theme, mode) {
67
+ function themeToCSS(theme, mode, tailwindVersion = "v4") {
67
68
  const colors = theme.colors[mode];
68
69
  if (!colors) return "";
69
70
  const parts = [];
70
71
  for (const key of CSS_TOKEN_KEYS) {
71
72
  const color = colors[key];
72
73
  if (color) {
73
- parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color)}`);
74
+ parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color, tailwindVersion)}`);
74
75
  }
75
76
  }
76
77
  if (theme.radius != null) {
@@ -78,12 +79,12 @@ function themeToCSS(theme, mode) {
78
79
  }
79
80
  return parts.join(";");
80
81
  }
81
- function themeToCSSBothModes(theme) {
82
- const lightCSS = themeToCSS(theme, "light");
83
- const darkCSS = themeToCSS(theme, "dark");
82
+ function themeToCSSBothModes(theme, tailwindVersion = "v4") {
83
+ const lightCSS = themeToCSS(theme, "light", tailwindVersion);
84
+ const darkCSS = themeToCSS(theme, "dark", tailwindVersion);
84
85
  return `:root{${lightCSS}}.dark{${darkCSS}}`;
85
86
  }
86
- function applyThemeCSSVariables(theme, mode) {
87
+ function applyThemeCSSVariables(theme, mode, tailwindVersion = "v4") {
87
88
  if (typeof document === "undefined") return;
88
89
  const colors = theme.colors[mode];
89
90
  if (!colors) return;
@@ -91,7 +92,7 @@ function applyThemeCSSVariables(theme, mode) {
91
92
  for (const key of CSS_TOKEN_KEYS) {
92
93
  const color = colors[key];
93
94
  if (color) {
94
- style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color));
95
+ style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color, tailwindVersion));
95
96
  }
96
97
  }
97
98
  if (theme.radius != null) {
@@ -247,7 +248,7 @@ function DhemeProvider({
247
248
  }, []);
248
249
  useEffect(() => {
249
250
  if (theme && autoApply) {
250
- applyThemeCSSVariables(theme, mode);
251
+ applyThemeCSSVariables(theme, mode, themeParams?.tailwindVersion ?? "v4");
251
252
  }
252
253
  if (typeof document !== "undefined") {
253
254
  if (mode === "dark") {
@@ -285,7 +286,7 @@ function DhemeProvider({
285
286
  setTheme(data);
286
287
  setIsReady(true);
287
288
  if (autoApplyRef.current) {
288
- applyThemeCSSVariables(data, modeRef.current);
289
+ applyThemeCSSVariables(data, modeRef.current, params.tailwindVersion ?? "v4");
289
290
  }
290
291
  if (persistRef.current) {
291
292
  const key = buildCacheKey(params);
@@ -324,7 +325,7 @@ function DhemeProvider({
324
325
  if (cached) {
325
326
  setTheme(cached);
326
327
  setIsReady(true);
327
- if (autoApply) applyThemeCSSVariables(cached, mode);
328
+ if (autoApply) applyThemeCSSVariables(cached, mode, params.tailwindVersion ?? "v4");
328
329
  const controller = new AbortController();
329
330
  abortRef.current = controller;
330
331
  fetchTheme(params).then((data) => {
@@ -333,7 +334,8 @@ function DhemeProvider({
333
334
  const freshLight = JSON.stringify(data.colors.light);
334
335
  if (cachedLight !== freshLight) {
335
336
  setTheme(data);
336
- if (autoApply) applyThemeCSSVariables(data, modeRef.current);
337
+ if (autoApply)
338
+ applyThemeCSSVariables(data, modeRef.current, params.tailwindVersion ?? "v4");
337
339
  saveThemeToCache(cacheKey, data);
338
340
  onThemeChangeRef.current?.(data);
339
341
  }
package/dist/utils.d.mts CHANGED
@@ -2,9 +2,9 @@ import { GenerateThemeResponse, GenerateThemeRequest } from '@dheme/sdk';
2
2
 
3
3
  type ThemeMode = 'light' | 'dark';
4
4
 
5
- declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode): string;
6
- declare function themeToCSSBothModes(theme: GenerateThemeResponse): string;
7
- declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode): void;
5
+ declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): string;
6
+ declare function themeToCSSBothModes(theme: GenerateThemeResponse, tailwindVersion?: 'v3' | 'v4'): string;
7
+ declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): void;
8
8
  declare function removeThemeCSSVariables(): void;
9
9
 
10
10
  declare function buildCacheKey(params: GenerateThemeRequest): string;
package/dist/utils.d.ts CHANGED
@@ -2,9 +2,9 @@ import { GenerateThemeResponse, GenerateThemeRequest } from '@dheme/sdk';
2
2
 
3
3
  type ThemeMode = 'light' | 'dark';
4
4
 
5
- declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode): string;
6
- declare function themeToCSSBothModes(theme: GenerateThemeResponse): string;
7
- declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode): void;
5
+ declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): string;
6
+ declare function themeToCSSBothModes(theme: GenerateThemeResponse, tailwindVersion?: 'v3' | 'v4'): string;
7
+ declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): void;
8
8
  declare function removeThemeCSSVariables(): void;
9
9
 
10
10
  declare function buildCacheKey(params: GenerateThemeRequest): string;
package/dist/utils.js CHANGED
@@ -75,17 +75,18 @@ var TOKEN_TO_CSS_VAR = {
75
75
  };
76
76
 
77
77
  // src/utils/cssVariables.ts
78
- function formatHSL(color) {
79
- return `hsl(${color.h} ${color.s}% ${color.l}%)`;
78
+ function formatHSL(color, tailwindVersion = "v4") {
79
+ const bare = `${color.h} ${color.s}% ${color.l}%`;
80
+ return tailwindVersion === "v3" ? bare : `hsl(${bare})`;
80
81
  }
81
- function themeToCSS(theme, mode) {
82
+ function themeToCSS(theme, mode, tailwindVersion = "v4") {
82
83
  const colors = theme.colors[mode];
83
84
  if (!colors) return "";
84
85
  const parts = [];
85
86
  for (const key of CSS_TOKEN_KEYS) {
86
87
  const color = colors[key];
87
88
  if (color) {
88
- parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color)}`);
89
+ parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color, tailwindVersion)}`);
89
90
  }
90
91
  }
91
92
  if (theme.radius != null) {
@@ -93,12 +94,12 @@ function themeToCSS(theme, mode) {
93
94
  }
94
95
  return parts.join(";");
95
96
  }
96
- function themeToCSSBothModes(theme) {
97
- const lightCSS = themeToCSS(theme, "light");
98
- const darkCSS = themeToCSS(theme, "dark");
97
+ function themeToCSSBothModes(theme, tailwindVersion = "v4") {
98
+ const lightCSS = themeToCSS(theme, "light", tailwindVersion);
99
+ const darkCSS = themeToCSS(theme, "dark", tailwindVersion);
99
100
  return `:root{${lightCSS}}.dark{${darkCSS}}`;
100
101
  }
101
- function applyThemeCSSVariables(theme, mode) {
102
+ function applyThemeCSSVariables(theme, mode, tailwindVersion = "v4") {
102
103
  if (typeof document === "undefined") return;
103
104
  const colors = theme.colors[mode];
104
105
  if (!colors) return;
@@ -106,7 +107,7 @@ function applyThemeCSSVariables(theme, mode) {
106
107
  for (const key of CSS_TOKEN_KEYS) {
107
108
  const color = colors[key];
108
109
  if (color) {
109
- style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color));
110
+ style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color, tailwindVersion));
110
111
  }
111
112
  }
112
113
  if (theme.radius != null) {
package/dist/utils.mjs CHANGED
@@ -43,17 +43,18 @@ var TOKEN_TO_CSS_VAR = {
43
43
  };
44
44
 
45
45
  // src/utils/cssVariables.ts
46
- function formatHSL(color) {
47
- return `hsl(${color.h} ${color.s}% ${color.l}%)`;
46
+ function formatHSL(color, tailwindVersion = "v4") {
47
+ const bare = `${color.h} ${color.s}% ${color.l}%`;
48
+ return tailwindVersion === "v3" ? bare : `hsl(${bare})`;
48
49
  }
49
- function themeToCSS(theme, mode) {
50
+ function themeToCSS(theme, mode, tailwindVersion = "v4") {
50
51
  const colors = theme.colors[mode];
51
52
  if (!colors) return "";
52
53
  const parts = [];
53
54
  for (const key of CSS_TOKEN_KEYS) {
54
55
  const color = colors[key];
55
56
  if (color) {
56
- parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color)}`);
57
+ parts.push(`${TOKEN_TO_CSS_VAR[key]}:${formatHSL(color, tailwindVersion)}`);
57
58
  }
58
59
  }
59
60
  if (theme.radius != null) {
@@ -61,12 +62,12 @@ function themeToCSS(theme, mode) {
61
62
  }
62
63
  return parts.join(";");
63
64
  }
64
- function themeToCSSBothModes(theme) {
65
- const lightCSS = themeToCSS(theme, "light");
66
- const darkCSS = themeToCSS(theme, "dark");
65
+ function themeToCSSBothModes(theme, tailwindVersion = "v4") {
66
+ const lightCSS = themeToCSS(theme, "light", tailwindVersion);
67
+ const darkCSS = themeToCSS(theme, "dark", tailwindVersion);
67
68
  return `:root{${lightCSS}}.dark{${darkCSS}}`;
68
69
  }
69
- function applyThemeCSSVariables(theme, mode) {
70
+ function applyThemeCSSVariables(theme, mode, tailwindVersion = "v4") {
70
71
  if (typeof document === "undefined") return;
71
72
  const colors = theme.colors[mode];
72
73
  if (!colors) return;
@@ -74,7 +75,7 @@ function applyThemeCSSVariables(theme, mode) {
74
75
  for (const key of CSS_TOKEN_KEYS) {
75
76
  const color = colors[key];
76
77
  if (color) {
77
- style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color));
78
+ style.setProperty(TOKEN_TO_CSS_VAR[key], formatHSL(color, tailwindVersion));
78
79
  }
79
80
  }
80
81
  if (theme.radius != null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dheme/react",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "description": "React bindings for Dheme SDK with zero-FOUC theme application",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",