@dheme/react 2.11.0 → 2.13.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.
@@ -297,15 +314,17 @@ The component renders as a pill in the corner of the screen. Clicking it expands
297
314
 
298
315
  ```tsx
299
316
  <ThemeGenerator
300
- defaultTheme="#4332f6" // Initial primary color
301
- defaultSecondaryColor="#ab67f1" // Initial secondary color
302
- defaultSaturation={10} // Initial saturation adjust (-100 to 100)
303
- defaultLightness={2} // Initial lightness adjust (-100 to 100)
304
- defaultRadius={0} // Initial border radius (0 to 2 rem)
305
- position="bottom-right" // 'bottom-right' | 'bottom-left'
306
- open={isOpen} // Controlled open state (optional)
307
- onOpenChange={setIsOpen} // Controlled open callback (optional)
308
- labels={{ // i18n overrides (all optional)
317
+ defaultTheme="#4332f6" // Initial primary color
318
+ defaultSecondaryColor="#ab67f1" // Initial secondary color
319
+ defaultSecondaryEnabled={false} // Whether secondary starts enabled
320
+ defaultSaturation={10} // Initial saturation adjust (-100 to 100)
321
+ defaultLightness={2} // Initial lightness adjust (-100 to 100)
322
+ defaultRadius={0} // Initial border radius (0 to 2 rem)
323
+ defaultBackgroundIsColored={false} // Initial colorful background toggle
324
+ position="bottom-right" // 'bottom-right' | 'bottom-left'
325
+ open={isOpen} // Controlled open state (optional)
326
+ onOpenChange={setIsOpen} // Controlled open callback (optional)
327
+ labels={{ // i18n overrides (all optional)
309
328
  title: 'Theme Generator',
310
329
  primary: 'Primary Color',
311
330
  secondary: 'Secondary Color',
@@ -313,7 +332,7 @@ The component renders as a pill in the corner of the screen. Clicking it expands
313
332
  lightness: 'Brightness',
314
333
  reset: 'Restore defaults',
315
334
  }}
316
- className="my-fab" // Extra class on the container (optional)
335
+ className="my-fab" // Extra class on the container (optional)
317
336
  />
318
337
  ```
319
338
 
@@ -321,9 +340,11 @@ The component renders as a pill in the corner of the screen. Clicking it expands
321
340
  | --- | --- | --- | --- |
322
341
  | `defaultTheme` | `string` | `'#4332f6'` | Initial primary HEX color. |
323
342
  | `defaultSecondaryColor` | `string` | `'#ab67f1'` | Initial secondary HEX color. |
343
+ | `defaultSecondaryEnabled` | `boolean` | `false` | Whether secondary color starts enabled. |
324
344
  | `defaultSaturation` | `number` | `10` | Initial saturation adjust (-100–100). |
325
345
  | `defaultLightness` | `number` | `2` | Initial lightness adjust (-100–100). |
326
346
  | `defaultRadius` | `number` | `0` | Initial border radius (0–2 rem). |
347
+ | `defaultBackgroundIsColored` | `boolean` | `false` | Initial state of the colorful background toggle. |
327
348
  | `position` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Corner to anchor the FAB. |
328
349
  | `open` | `boolean` | — | Controlled open state. Omit for uncontrolled. |
329
350
  | `onOpenChange` | `(open: boolean) => void` | — | Called when open state changes. |
@@ -409,25 +430,31 @@ const [open, setOpen] = useState(false);
409
430
 
410
431
  ## Utilities
411
432
 
412
- ### `themeToCSS(theme, mode)`
433
+ ### `themeToCSS(theme, mode, tailwindVersion?)`
413
434
 
414
435
  Convert a `GenerateThemeResponse` to a CSS variable assignment string.
415
436
 
416
437
  ```typescript
417
438
  import { themeToCSS } from '@dheme/react';
418
439
 
440
+ // Tailwind v4 (default) — hsl() wrapped
419
441
  const css = themeToCSS(theme, 'light');
442
+ // "--background:hsl(0 0% 100%);--foreground:hsl(222.2 84% 4.9%);..."
443
+
444
+ // Tailwind v3 — bare channels
445
+ const css = themeToCSS(theme, 'light', 'v3');
420
446
  // "--background:0 0% 100%;--foreground:222.2 84% 4.9%;..."
421
447
  ```
422
448
 
423
- ### `applyThemeCSSVariables(theme, mode)`
449
+ ### `applyThemeCSSVariables(theme, mode, tailwindVersion?)`
424
450
 
425
451
  Manually apply CSS variables to `:root`.
426
452
 
427
453
  ```typescript
428
454
  import { applyThemeCSSVariables } from '@dheme/react';
429
455
 
430
- applyThemeCSSVariables(theme, 'dark');
456
+ applyThemeCSSVariables(theme, 'dark'); // Tailwind v4 (default)
457
+ applyThemeCSSVariables(theme, 'dark', 'v3'); // Tailwind v3
431
458
  ```
432
459
 
433
460
  ### `removeThemeCSSVariables()`
package/dist/index.d.mts CHANGED
@@ -67,9 +67,11 @@ declare function DhemeScript({ defaultMode, nonce, }: DhemeScriptProps): React__
67
67
  interface ThemeGeneratorProps {
68
68
  defaultTheme?: string;
69
69
  defaultSecondaryColor?: string;
70
+ defaultSecondaryEnabled?: boolean;
70
71
  defaultSaturation?: number;
71
72
  defaultLightness?: number;
72
73
  defaultRadius?: number;
74
+ defaultBackgroundIsColored?: boolean;
73
75
  position?: 'bottom-right' | 'bottom-left';
74
76
  open?: boolean;
75
77
  onOpenChange?: (open: boolean) => void;
@@ -93,7 +95,7 @@ interface ThemeGeneratorProps {
93
95
  };
94
96
  className?: string;
95
97
  }
96
- declare function ThemeGenerator({ defaultTheme, defaultSecondaryColor, defaultSaturation, defaultLightness, defaultRadius, position, open: controlledOpen, onOpenChange, labels: labelsProp, className, }: ThemeGeneratorProps): React__default.ReactElement;
98
+ declare function ThemeGenerator({ defaultTheme, defaultSecondaryColor, defaultSecondaryEnabled, defaultSaturation, defaultLightness, defaultRadius, defaultBackgroundIsColored, position, open: controlledOpen, onOpenChange, labels: labelsProp, className, }: ThemeGeneratorProps): React__default.ReactElement;
97
99
 
98
100
  declare function useTheme(): ThemeDataState;
99
101
 
@@ -107,9 +109,9 @@ declare function useGenerateTheme(): {
107
109
 
108
110
  declare function useDhemeClient(): DhemeClient;
109
111
 
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;
112
+ declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): string;
113
+ declare function themeToCSSBothModes(theme: GenerateThemeResponse, tailwindVersion?: 'v3' | 'v4'): string;
114
+ declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): void;
113
115
  declare function removeThemeCSSVariables(): void;
114
116
 
115
117
  declare function buildCacheKey(params: GenerateThemeRequest): string;
package/dist/index.d.ts CHANGED
@@ -67,9 +67,11 @@ declare function DhemeScript({ defaultMode, nonce, }: DhemeScriptProps): React__
67
67
  interface ThemeGeneratorProps {
68
68
  defaultTheme?: string;
69
69
  defaultSecondaryColor?: string;
70
+ defaultSecondaryEnabled?: boolean;
70
71
  defaultSaturation?: number;
71
72
  defaultLightness?: number;
72
73
  defaultRadius?: number;
74
+ defaultBackgroundIsColored?: boolean;
73
75
  position?: 'bottom-right' | 'bottom-left';
74
76
  open?: boolean;
75
77
  onOpenChange?: (open: boolean) => void;
@@ -93,7 +95,7 @@ interface ThemeGeneratorProps {
93
95
  };
94
96
  className?: string;
95
97
  }
96
- declare function ThemeGenerator({ defaultTheme, defaultSecondaryColor, defaultSaturation, defaultLightness, defaultRadius, position, open: controlledOpen, onOpenChange, labels: labelsProp, className, }: ThemeGeneratorProps): React__default.ReactElement;
98
+ declare function ThemeGenerator({ defaultTheme, defaultSecondaryColor, defaultSecondaryEnabled, defaultSaturation, defaultLightness, defaultRadius, defaultBackgroundIsColored, position, open: controlledOpen, onOpenChange, labels: labelsProp, className, }: ThemeGeneratorProps): React__default.ReactElement;
97
99
 
98
100
  declare function useTheme(): ThemeDataState;
99
101
 
@@ -107,9 +109,9 @@ declare function useGenerateTheme(): {
107
109
 
108
110
  declare function useDhemeClient(): DhemeClient;
109
111
 
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;
112
+ declare function themeToCSS(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): string;
113
+ declare function themeToCSSBothModes(theme: GenerateThemeResponse, tailwindVersion?: 'v3' | 'v4'): string;
114
+ declare function applyThemeCSSVariables(theme: GenerateThemeResponse, mode: ThemeMode, tailwindVersion?: 'v3' | 'v4'): void;
113
115
  declare function removeThemeCSSVariables(): void;
114
116
 
115
117
  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
  }
@@ -1013,9 +1015,11 @@ function cn(...classes) {
1013
1015
  function ThemeGenerator({
1014
1016
  defaultTheme = "#4332f6",
1015
1017
  defaultSecondaryColor = "#ab67f1",
1018
+ defaultSecondaryEnabled = false,
1016
1019
  defaultSaturation = 10,
1017
1020
  defaultLightness = 2,
1018
1021
  defaultRadius = 0,
1022
+ defaultBackgroundIsColored = false,
1019
1023
  position = "bottom-right",
1020
1024
  open: controlledOpen,
1021
1025
  onOpenChange,
@@ -1053,12 +1057,12 @@ function ThemeGenerator({
1053
1057
  );
1054
1058
  const [localPrimary, setLocalPrimary] = (0, import_react7.useState)(defaultTheme);
1055
1059
  const [localSecondary, setLocalSecondary] = (0, import_react7.useState)(defaultSecondaryColor);
1056
- const [isSecondaryEnabled, setIsSecondaryEnabled] = (0, import_react7.useState)(false);
1060
+ const [isSecondaryEnabled, setIsSecondaryEnabled] = (0, import_react7.useState)(defaultSecondaryEnabled);
1057
1061
  const [localSaturation, setLocalSaturation] = (0, import_react7.useState)([defaultSaturation]);
1058
1062
  const [localLightness, setLocalLightness] = (0, import_react7.useState)([defaultLightness]);
1059
1063
  const [localRadius, setLocalRadius] = (0, import_react7.useState)([defaultRadius]);
1060
1064
  const [localCardIsColored, setLocalCardIsColored] = (0, import_react7.useState)(false);
1061
- const [localBackgroundIsColored, setLocalBackgroundIsColored] = (0, import_react7.useState)(false);
1065
+ const [localBackgroundIsColored, setLocalBackgroundIsColored] = (0, import_react7.useState)(defaultBackgroundIsColored);
1062
1066
  const [localBorderIsColored, setLocalBorderIsColored] = (0, import_react7.useState)(false);
1063
1067
  const paramsRef = (0, import_react7.useRef)({
1064
1068
  theme: localPrimary,
@@ -1131,17 +1135,18 @@ function ThemeGenerator({
1131
1135
  setLocalSaturation([defaultSaturation]);
1132
1136
  setLocalLightness([defaultLightness]);
1133
1137
  setLocalRadius([defaultRadius]);
1134
- setIsSecondaryEnabled(false);
1138
+ setIsSecondaryEnabled(defaultSecondaryEnabled);
1135
1139
  setLocalCardIsColored(false);
1136
- setLocalBackgroundIsColored(false);
1140
+ setLocalBackgroundIsColored(defaultBackgroundIsColored);
1137
1141
  setLocalBorderIsColored(false);
1138
1142
  generateTheme({
1139
1143
  theme: defaultTheme,
1144
+ secondaryColor: defaultSecondaryEnabled ? defaultSecondaryColor : void 0,
1140
1145
  saturationAdjust: defaultSaturation,
1141
1146
  lightnessAdjust: defaultLightness,
1142
1147
  radius: defaultRadius,
1143
1148
  cardIsColored: false,
1144
- backgroundIsColored: false,
1149
+ backgroundIsColored: defaultBackgroundIsColored,
1145
1150
  borderIsColored: false
1146
1151
  });
1147
1152
  };
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
  }
@@ -963,9 +965,11 @@ function cn(...classes) {
963
965
  function ThemeGenerator({
964
966
  defaultTheme = "#4332f6",
965
967
  defaultSecondaryColor = "#ab67f1",
968
+ defaultSecondaryEnabled = false,
966
969
  defaultSaturation = 10,
967
970
  defaultLightness = 2,
968
971
  defaultRadius = 0,
972
+ defaultBackgroundIsColored = false,
969
973
  position = "bottom-right",
970
974
  open: controlledOpen,
971
975
  onOpenChange,
@@ -1003,12 +1007,12 @@ function ThemeGenerator({
1003
1007
  );
1004
1008
  const [localPrimary, setLocalPrimary] = useState3(defaultTheme);
1005
1009
  const [localSecondary, setLocalSecondary] = useState3(defaultSecondaryColor);
1006
- const [isSecondaryEnabled, setIsSecondaryEnabled] = useState3(false);
1010
+ const [isSecondaryEnabled, setIsSecondaryEnabled] = useState3(defaultSecondaryEnabled);
1007
1011
  const [localSaturation, setLocalSaturation] = useState3([defaultSaturation]);
1008
1012
  const [localLightness, setLocalLightness] = useState3([defaultLightness]);
1009
1013
  const [localRadius, setLocalRadius] = useState3([defaultRadius]);
1010
1014
  const [localCardIsColored, setLocalCardIsColored] = useState3(false);
1011
- const [localBackgroundIsColored, setLocalBackgroundIsColored] = useState3(false);
1015
+ const [localBackgroundIsColored, setLocalBackgroundIsColored] = useState3(defaultBackgroundIsColored);
1012
1016
  const [localBorderIsColored, setLocalBorderIsColored] = useState3(false);
1013
1017
  const paramsRef = useRef2({
1014
1018
  theme: localPrimary,
@@ -1081,17 +1085,18 @@ function ThemeGenerator({
1081
1085
  setLocalSaturation([defaultSaturation]);
1082
1086
  setLocalLightness([defaultLightness]);
1083
1087
  setLocalRadius([defaultRadius]);
1084
- setIsSecondaryEnabled(false);
1088
+ setIsSecondaryEnabled(defaultSecondaryEnabled);
1085
1089
  setLocalCardIsColored(false);
1086
- setLocalBackgroundIsColored(false);
1090
+ setLocalBackgroundIsColored(defaultBackgroundIsColored);
1087
1091
  setLocalBorderIsColored(false);
1088
1092
  generateTheme({
1089
1093
  theme: defaultTheme,
1094
+ secondaryColor: defaultSecondaryEnabled ? defaultSecondaryColor : void 0,
1090
1095
  saturationAdjust: defaultSaturation,
1091
1096
  lightnessAdjust: defaultLightness,
1092
1097
  radius: defaultRadius,
1093
1098
  cardIsColored: false,
1094
- backgroundIsColored: false,
1099
+ backgroundIsColored: defaultBackgroundIsColored,
1095
1100
  borderIsColored: false
1096
1101
  });
1097
1102
  };
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.13.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",