gradient-forge 1.0.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.
Files changed (56) hide show
  1. package/.eslintrc.json +3 -0
  2. package/.github/FUNDING.yml +2 -0
  3. package/README.md +140 -0
  4. package/app/docs/page.tsx +417 -0
  5. package/app/gallery/page.tsx +398 -0
  6. package/app/globals.css +1155 -0
  7. package/app/layout.tsx +36 -0
  8. package/app/page.tsx +600 -0
  9. package/app/showcase/page.tsx +730 -0
  10. package/app/studio/page.tsx +1310 -0
  11. package/cli/index.mjs +1141 -0
  12. package/cli/templates/theme-context.tsx +120 -0
  13. package/cli/templates/theme-engine.ts +237 -0
  14. package/cli/templates/themes.css +512 -0
  15. package/components/site/component-showcase.tsx +623 -0
  16. package/components/site/site-data.ts +103 -0
  17. package/components/site/site-header.tsx +270 -0
  18. package/components/templates/blog.tsx +198 -0
  19. package/components/templates/components-showcase.tsx +298 -0
  20. package/components/templates/dashboard.tsx +246 -0
  21. package/components/templates/ecommerce.tsx +199 -0
  22. package/components/templates/mail.tsx +275 -0
  23. package/components/templates/saas-landing.tsx +169 -0
  24. package/components/theme/studio-code-panel.tsx +485 -0
  25. package/components/theme/theme-context.tsx +120 -0
  26. package/components/theme/theme-engine.ts +237 -0
  27. package/components/theme/theme-exporter.tsx +369 -0
  28. package/components/theme/theme-panel.tsx +268 -0
  29. package/components/theme/token-export-utils.ts +1211 -0
  30. package/components/ui/animated.tsx +55 -0
  31. package/components/ui/avatar.tsx +38 -0
  32. package/components/ui/badge.tsx +32 -0
  33. package/components/ui/button.tsx +65 -0
  34. package/components/ui/card.tsx +56 -0
  35. package/components/ui/checkbox.tsx +19 -0
  36. package/components/ui/command-palette.tsx +245 -0
  37. package/components/ui/gsap-animated.tsx +436 -0
  38. package/components/ui/input.tsx +17 -0
  39. package/components/ui/select.tsx +176 -0
  40. package/components/ui/skeleton.tsx +102 -0
  41. package/components/ui/switch.tsx +43 -0
  42. package/components/ui/tabs.tsx +115 -0
  43. package/components/ui/toast.tsx +119 -0
  44. package/gradient-forge/theme-context.tsx +119 -0
  45. package/gradient-forge/theme-engine.ts +236 -0
  46. package/gradient-forge/themes.css +556 -0
  47. package/lib/animations.ts +50 -0
  48. package/lib/gsap.ts +426 -0
  49. package/lib/utils.ts +6 -0
  50. package/next-env.d.ts +6 -0
  51. package/next.config.mjs +6 -0
  52. package/package.json +53 -0
  53. package/postcss.config.mjs +5 -0
  54. package/tailwind.config.ts +15 -0
  55. package/tsconfig.json +43 -0
  56. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,120 @@
1
+ "use client";
2
+
3
+ import {
4
+ type ColorMode,
5
+ type ThemeId,
6
+ MEMORY_LANE_THEME,
7
+ NITRO_ALL_THEMES,
8
+ NITRO_PUBLIC_THEMES,
9
+ applyTheme,
10
+ defaultColorMode,
11
+ defaultTheme,
12
+ getStoredColorMode,
13
+ getStoredTheme,
14
+ persistTourProgress,
15
+ publicThemeIdSet,
16
+ publicThemeIds,
17
+ readTourProgress,
18
+ } from "./theme-engine";
19
+ import { createContext, useContext, useEffect, useMemo, useState } from "react";
20
+
21
+ type ThemeContextValue = {
22
+ themeId: ThemeId;
23
+ colorMode: ColorMode;
24
+ availableThemes: typeof NITRO_ALL_THEMES;
25
+ viewedThemeIds: string[];
26
+ memoryLaneUnlocked: boolean;
27
+ remainingForUnlock: number;
28
+ setThemeId: (themeId: ThemeId) => void;
29
+ setColorMode: (mode: ColorMode) => void;
30
+ };
31
+
32
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
33
+
34
+ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
35
+ const [themeId, setThemeIdState] = useState<ThemeId>(defaultTheme);
36
+ const [colorMode, setColorModeState] = useState<ColorMode>(defaultColorMode);
37
+ const [viewedThemeIds, setViewedThemeIds] = useState<string[]>([]);
38
+ const [memoryLaneUnlocked, setMemoryLaneUnlocked] = useState(false);
39
+
40
+ useEffect(() => {
41
+ const storedTheme = getStoredTheme();
42
+ const storedMode = getStoredColorMode();
43
+ const tour = readTourProgress();
44
+
45
+ setThemeIdState(storedTheme);
46
+ setColorModeState(storedMode);
47
+ setViewedThemeIds(tour.viewedThemeIds);
48
+ setMemoryLaneUnlocked(tour.memoryLaneUnlocked);
49
+ applyTheme(storedTheme, storedMode);
50
+ }, []);
51
+
52
+ useEffect(() => {
53
+ applyTheme(themeId, colorMode);
54
+ }, [themeId, colorMode]);
55
+
56
+ const setThemeId = (nextTheme: ThemeId) => {
57
+ const isPublic = publicThemeIdSet.has(nextTheme);
58
+ const nextViewedThemeIds = isPublic
59
+ ? Array.from(new Set([...viewedThemeIds, nextTheme]))
60
+ : viewedThemeIds;
61
+
62
+ const hasCompletedTour = publicThemeIds.every((id) =>
63
+ nextViewedThemeIds.includes(id),
64
+ );
65
+ const nextMemoryLaneUnlocked = memoryLaneUnlocked || hasCompletedTour;
66
+
67
+ setThemeIdState(nextTheme);
68
+ setViewedThemeIds(nextViewedThemeIds);
69
+ setMemoryLaneUnlocked(nextMemoryLaneUnlocked);
70
+ persistTourProgress({
71
+ viewedThemeIds: nextViewedThemeIds,
72
+ memoryLaneUnlocked: nextMemoryLaneUnlocked,
73
+ });
74
+ };
75
+
76
+ const setColorMode = (mode: ColorMode) => {
77
+ setColorModeState(mode);
78
+ };
79
+
80
+ const availableThemes = useMemo(
81
+ () => (memoryLaneUnlocked ? NITRO_ALL_THEMES : NITRO_PUBLIC_THEMES),
82
+ [memoryLaneUnlocked],
83
+ );
84
+
85
+ const remainingForUnlock = useMemo(() => {
86
+ const remaining = publicThemeIds.length - viewedThemeIds.length;
87
+ return Math.max(0, remaining);
88
+ }, [viewedThemeIds]);
89
+
90
+ useEffect(() => {
91
+ if (themeId === MEMORY_LANE_THEME.id && !memoryLaneUnlocked) {
92
+ setMemoryLaneUnlocked(true);
93
+ }
94
+ }, [themeId, memoryLaneUnlocked]);
95
+
96
+ return (
97
+ <ThemeContext.Provider
98
+ value={{
99
+ themeId,
100
+ colorMode,
101
+ availableThemes: availableThemes as typeof NITRO_ALL_THEMES,
102
+ viewedThemeIds,
103
+ memoryLaneUnlocked,
104
+ remainingForUnlock,
105
+ setThemeId,
106
+ setColorMode,
107
+ }}
108
+ >
109
+ {children}
110
+ </ThemeContext.Provider>
111
+ );
112
+ };
113
+
114
+ export const useThemeContext = () => {
115
+ const ctx = useContext(ThemeContext);
116
+ if (!ctx) {
117
+ throw new Error("useThemeContext must be used within ThemeProvider");
118
+ }
119
+ return ctx;
120
+ };
@@ -0,0 +1,237 @@
1
+ export const NITRO_PUBLIC_THEMES = [
2
+ {
3
+ id: "theme-nitro-mint-apple",
4
+ label: "Mint Apple",
5
+ preview:
6
+ "radial-gradient(circle at 12% 10%, #b2ffe1 0%, transparent 42%), linear-gradient(145deg, #2d8e74 0%, #70c76a 46%, #d5ef91 100%)",
7
+ },
8
+ {
9
+ id: "theme-nitro-citrus-sherbert",
10
+ label: "Citrus Sherbert",
11
+ preview:
12
+ "radial-gradient(circle at 10% 10%, #ffd37d 0%, transparent 42%), linear-gradient(145deg, #e67d35 0%, #f7bb54 46%, #fff0a3 100%)",
13
+ },
14
+ {
15
+ id: "theme-nitro-retro-raincloud",
16
+ label: "Retro Raincloud",
17
+ preview:
18
+ "radial-gradient(circle at 12% 10%, #b3c6dc 0%, transparent 42%), linear-gradient(145deg, #4e6077 0%, #6f86a0 46%, #98aec3 100%)",
19
+ },
20
+ {
21
+ id: "theme-nitro-hanami",
22
+ label: "Hanami",
23
+ preview:
24
+ "radial-gradient(circle at 12% 10%, #ffd5e7 0%, transparent 42%), linear-gradient(145deg, #995382 0%, #c77ca9 46%, #f0b7ce 100%)",
25
+ },
26
+ {
27
+ id: "theme-nitro-sunrise",
28
+ label: "Sunrise",
29
+ preview:
30
+ "radial-gradient(circle at 12% 10%, #ffb596 0%, transparent 42%), linear-gradient(145deg, #e25263 0%, #ef8a57 46%, #ffd07a 100%)",
31
+ },
32
+ {
33
+ id: "theme-nitro-cotton-candy",
34
+ label: "Cotton Candy",
35
+ preview:
36
+ "radial-gradient(circle at 12% 10%, #c8e9ff 0%, transparent 42%), linear-gradient(145deg, #5aa2ff 0%, #9b78f0 46%, #f39bca 100%)",
37
+ },
38
+ {
39
+ id: "theme-nitro-lofi-vibes",
40
+ label: "Lofi Vibes",
41
+ preview:
42
+ "radial-gradient(circle at 10% 10%, #8e97c6 0%, transparent 42%), linear-gradient(145deg, #3f476c 0%, #59608f 46%, #7a6f9f 100%)",
43
+ },
44
+ {
45
+ id: "theme-nitro-desert-khaki",
46
+ label: "Desert Khaki",
47
+ preview:
48
+ "radial-gradient(circle at 10% 10%, #d5bf92 0%, transparent 42%), linear-gradient(145deg, #6d5c49 0%, #8f7a5d 46%, #b49f76 100%)",
49
+ },
50
+ {
51
+ id: "theme-nitro-sunset",
52
+ label: "Sunset",
53
+ preview:
54
+ "radial-gradient(circle at 10% 10%, #d66a82 0%, transparent 42%), linear-gradient(145deg, #3f1b4d 0%, #8c335f 46%, #f4874f 100%)",
55
+ },
56
+ {
57
+ id: "theme-nitro-chroma-glow",
58
+ label: "Chroma Glow",
59
+ preview:
60
+ "radial-gradient(circle at 10% 10%, #7f8dff 0%, transparent 42%), linear-gradient(145deg, #2d3eff 0%, #a726fa 46%, #00c7ff 100%)",
61
+ },
62
+ {
63
+ id: "theme-nitro-forest",
64
+ label: "Forest",
65
+ preview:
66
+ "radial-gradient(circle at 10% 10%, #56be87 0%, transparent 42%), linear-gradient(145deg, #163f2e 0%, #246b49 46%, #59a86c 100%)",
67
+ },
68
+ {
69
+ id: "theme-nitro-crimson",
70
+ label: "Crimson",
71
+ preview:
72
+ "radial-gradient(circle at 10% 10%, #c23956 0%, transparent 42%), linear-gradient(145deg, #2d050f 0%, #681126 46%, #a82435 100%)",
73
+ },
74
+ {
75
+ id: "theme-nitro-midnight-blurple",
76
+ label: "Midnight Blurple",
77
+ preview:
78
+ "radial-gradient(circle at 10% 10%, #6d6eff 0%, transparent 42%), linear-gradient(145deg, #0f1232 0%, #25366f 46%, #5757dc 100%)",
79
+ },
80
+ {
81
+ id: "theme-nitro-mars",
82
+ label: "Mars",
83
+ preview:
84
+ "radial-gradient(circle at 10% 10%, #cc654c 0%, transparent 42%), linear-gradient(145deg, #2e140f 0%, #5e261d 46%, #9c422f 100%)",
85
+ },
86
+ {
87
+ id: "theme-nitro-dusk",
88
+ label: "Dusk",
89
+ preview:
90
+ "radial-gradient(circle at 10% 10%, #9475c1 0%, transparent 42%), linear-gradient(145deg, #1b1632 0%, #3b2d5b 46%, #745495 100%)",
91
+ },
92
+ {
93
+ id: "theme-nitro-under-the-sea",
94
+ label: "Under The Sea",
95
+ preview:
96
+ "radial-gradient(circle at 10% 10%, #3ea6b7 0%, transparent 42%), linear-gradient(145deg, #0b2242 0%, #0d4f69 46%, #2b848e 100%)",
97
+ },
98
+ {
99
+ id: "theme-nitro-retro-storm",
100
+ label: "Retro Storm",
101
+ preview:
102
+ "radial-gradient(circle at 10% 10%, #7e95ab 0%, transparent 42%), linear-gradient(145deg, #1d2b3a 0%, #354657 46%, #55667a 100%)",
103
+ },
104
+ {
105
+ id: "theme-nitro-neon-nights",
106
+ label: "Neon Nights",
107
+ preview:
108
+ "radial-gradient(circle at 10% 10%, #d13eff 0%, transparent 42%), linear-gradient(145deg, #05061a 0%, #180f52 46%, #00bde6 100%)",
109
+ },
110
+ {
111
+ id: "theme-nitro-strawberry-lemonade",
112
+ label: "Strawberry Lemonade",
113
+ preview:
114
+ "radial-gradient(circle at 10% 10%, #ff8aa8 0%, transparent 42%), linear-gradient(145deg, #8f1847 0%, #cc3f5e 46%, #efc141 100%)",
115
+ },
116
+ {
117
+ id: "theme-nitro-aurora",
118
+ label: "Aurora",
119
+ preview:
120
+ "radial-gradient(circle at 10% 10%, #41cbb1 0%, transparent 42%), linear-gradient(145deg, #083142 0%, #1b7e74 46%, #5fbf75 100%)",
121
+ },
122
+ {
123
+ id: "theme-nitro-sepia",
124
+ label: "Sepia",
125
+ preview:
126
+ "radial-gradient(circle at 10% 10%, #b99672 0%, transparent 42%), linear-gradient(145deg, #35261d 0%, #5d4636 46%, #927454 100%)",
127
+ },
128
+ ] as const;
129
+
130
+ export const MEMORY_LANE_THEME = {
131
+ id: "theme-nitro-memory-lane",
132
+ label: "Memory Lane",
133
+ preview:
134
+ "radial-gradient(circle at 10% 10%, #ba8fd4 0%, transparent 42%), linear-gradient(145deg, #462d42 0%, #684b75 35%, #4a7199 68%, #77ad9f 100%)",
135
+ } as const;
136
+
137
+ export const NITRO_ALL_THEMES = [
138
+ ...NITRO_PUBLIC_THEMES,
139
+ MEMORY_LANE_THEME,
140
+ ] as const;
141
+
142
+ export type ThemeId = (typeof NITRO_ALL_THEMES)[number]["id"];
143
+ export type ColorMode = "dark" | "light";
144
+
145
+ type ThemeTourProgress = {
146
+ viewedThemeIds: string[];
147
+ memoryLaneUnlocked: boolean;
148
+ };
149
+
150
+ const DEFAULT_THEME: ThemeId = "theme-nitro-midnight-blurple";
151
+ const DEFAULT_COLOR_MODE: ColorMode = "dark";
152
+ const THEME_STORAGE_KEY = "shadcn-gradient.theme";
153
+ const COLOR_MODE_STORAGE_KEY = "shadcn-gradient.color-mode";
154
+ const THEME_TOUR_STORAGE_KEY = "shadcn-gradient.theme-tour.v1";
155
+
156
+ const NITRO_THEME_IDS = new Set<string>(NITRO_ALL_THEMES.map((theme) => theme.id));
157
+
158
+ export const normalizeTheme = (theme?: string | null): ThemeId => {
159
+ if (!theme || !NITRO_THEME_IDS.has(theme)) {
160
+ return DEFAULT_THEME;
161
+ }
162
+
163
+ return theme as ThemeId;
164
+ };
165
+
166
+ export const normalizeColorMode = (mode?: string | null): ColorMode => {
167
+ if (mode === "light") return "light";
168
+ return DEFAULT_COLOR_MODE;
169
+ };
170
+
171
+ export const getStoredTheme = (): ThemeId => {
172
+ if (typeof window === "undefined") return DEFAULT_THEME;
173
+ return normalizeTheme(localStorage.getItem(THEME_STORAGE_KEY));
174
+ };
175
+
176
+ export const getStoredColorMode = (): ColorMode => {
177
+ if (typeof window === "undefined") return DEFAULT_COLOR_MODE;
178
+ return normalizeColorMode(localStorage.getItem(COLOR_MODE_STORAGE_KEY));
179
+ };
180
+
181
+ export const readTourProgress = (): ThemeTourProgress => {
182
+ if (typeof window === "undefined") {
183
+ return { viewedThemeIds: [], memoryLaneUnlocked: false };
184
+ }
185
+
186
+ const raw = localStorage.getItem(THEME_TOUR_STORAGE_KEY);
187
+ if (!raw) {
188
+ return { viewedThemeIds: [], memoryLaneUnlocked: false };
189
+ }
190
+
191
+ try {
192
+ const parsed = JSON.parse(raw) as ThemeTourProgress;
193
+ return {
194
+ viewedThemeIds: Array.isArray(parsed.viewedThemeIds)
195
+ ? parsed.viewedThemeIds.filter((id) => NITRO_THEME_IDS.has(id))
196
+ : [],
197
+ memoryLaneUnlocked: Boolean(parsed.memoryLaneUnlocked),
198
+ };
199
+ } catch {
200
+ return { viewedThemeIds: [], memoryLaneUnlocked: false };
201
+ }
202
+ };
203
+
204
+ export const persistTourProgress = (progress: ThemeTourProgress) => {
205
+ if (typeof window === "undefined") return;
206
+ localStorage.setItem(THEME_TOUR_STORAGE_KEY, JSON.stringify(progress));
207
+ };
208
+
209
+ export const applyTheme = (theme?: string | null, mode?: ColorMode) => {
210
+ if (typeof window === "undefined") return;
211
+
212
+ const normalizedTheme = normalizeTheme(theme);
213
+ const normalizedMode = normalizeColorMode(mode ?? getStoredColorMode());
214
+ const root = document.documentElement;
215
+
216
+ Array.from(root.classList).forEach((className) => {
217
+ if (
218
+ className === "dark" ||
219
+ className === "light" ||
220
+ className.startsWith("theme-nitro-")
221
+ ) {
222
+ root.classList.remove(className);
223
+ }
224
+ });
225
+
226
+ root.classList.add(normalizedMode, normalizedTheme);
227
+ root.setAttribute("data-theme", normalizedTheme);
228
+ root.setAttribute("data-color-mode", normalizedMode);
229
+
230
+ localStorage.setItem(THEME_STORAGE_KEY, normalizedTheme);
231
+ localStorage.setItem(COLOR_MODE_STORAGE_KEY, normalizedMode);
232
+ };
233
+
234
+ export const defaultTheme = DEFAULT_THEME;
235
+ export const defaultColorMode = DEFAULT_COLOR_MODE;
236
+ export const publicThemeIds = NITRO_PUBLIC_THEMES.map((theme) => theme.id);
237
+ export const publicThemeIdSet = new Set<string>(publicThemeIds);