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
package/lib/gsap.ts ADDED
@@ -0,0 +1,426 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef } from "react";
4
+ import gsap from "gsap";
5
+ import { ScrollTrigger } from "gsap/ScrollTrigger";
6
+
7
+ // Register GSAP plugins
8
+ if (typeof window !== "undefined") {
9
+ gsap.registerPlugin(ScrollTrigger);
10
+ }
11
+
12
+ // Hook for fade in up animation
13
+ export function useFadeInUp<T extends HTMLElement>() {
14
+ const ref = useRef<T>(null);
15
+
16
+ useEffect(() => {
17
+ const element = ref.current;
18
+ if (!element) return;
19
+
20
+ gsap.fromTo(
21
+ element,
22
+ { opacity: 0, y: 30 },
23
+ {
24
+ opacity: 1,
25
+ y: 0,
26
+ duration: 0.6,
27
+ ease: "power3.out",
28
+ scrollTrigger: {
29
+ trigger: element,
30
+ start: "top 85%",
31
+ toggleActions: "play none none none",
32
+ },
33
+ }
34
+ );
35
+
36
+ return () => {
37
+ ScrollTrigger.getAll().forEach((trigger) => {
38
+ if (trigger.trigger === element) {
39
+ trigger.kill();
40
+ }
41
+ });
42
+ };
43
+ }, []);
44
+
45
+ return ref;
46
+ }
47
+
48
+ // Hook for stagger animation
49
+ export function useStagger<T extends HTMLElement>(staggerAmount: number = 0.1) {
50
+ const containerRef = useRef<T>(null);
51
+
52
+ useEffect(() => {
53
+ const container = containerRef.current;
54
+ if (!container) return;
55
+
56
+ const children = container.children;
57
+ if (children.length === 0) return;
58
+
59
+ gsap.fromTo(
60
+ children,
61
+ { opacity: 0, y: 20 },
62
+ {
63
+ opacity: 1,
64
+ y: 0,
65
+ duration: 0.5,
66
+ stagger: staggerAmount,
67
+ ease: "power3.out",
68
+ scrollTrigger: {
69
+ trigger: container,
70
+ start: "top 80%",
71
+ toggleActions: "play none none none",
72
+ },
73
+ }
74
+ );
75
+
76
+ return () => {
77
+ ScrollTrigger.getAll().forEach((trigger) => {
78
+ if (trigger.trigger === container) {
79
+ trigger.kill();
80
+ }
81
+ });
82
+ };
83
+ }, [staggerAmount]);
84
+
85
+ return containerRef;
86
+ }
87
+
88
+ // Hook for scale in animation
89
+ export function useScaleIn<T extends HTMLElement>() {
90
+ const ref = useRef<T>(null);
91
+
92
+ useEffect(() => {
93
+ const element = ref.current;
94
+ if (!element) return;
95
+
96
+ gsap.fromTo(
97
+ element,
98
+ { opacity: 0, scale: 0.9 },
99
+ {
100
+ opacity: 1,
101
+ scale: 1,
102
+ duration: 0.5,
103
+ ease: "back.out(1.7)",
104
+ scrollTrigger: {
105
+ trigger: element,
106
+ start: "top 85%",
107
+ toggleActions: "play none none none",
108
+ },
109
+ }
110
+ );
111
+
112
+ return () => {
113
+ ScrollTrigger.getAll().forEach((trigger) => {
114
+ if (trigger.trigger === element) {
115
+ trigger.kill();
116
+ }
117
+ });
118
+ };
119
+ }, []);
120
+
121
+ return ref;
122
+ }
123
+
124
+ // Hook for slide in from left
125
+ export function useSlideInLeft<T extends HTMLElement>() {
126
+ const ref = useRef<T>(null);
127
+
128
+ useEffect(() => {
129
+ const element = ref.current;
130
+ if (!element) return;
131
+
132
+ gsap.fromTo(
133
+ element,
134
+ { opacity: 0, x: -50 },
135
+ {
136
+ opacity: 1,
137
+ x: 0,
138
+ duration: 0.6,
139
+ ease: "power3.out",
140
+ scrollTrigger: {
141
+ trigger: element,
142
+ start: "top 85%",
143
+ toggleActions: "play none none none",
144
+ },
145
+ }
146
+ );
147
+
148
+ return () => {
149
+ ScrollTrigger.getAll().forEach((trigger) => {
150
+ if (trigger.trigger === element) {
151
+ trigger.kill();
152
+ }
153
+ });
154
+ };
155
+ }, []);
156
+
157
+ return ref;
158
+ }
159
+
160
+ // Hook for slide in from right
161
+ export function useSlideInRight<T extends HTMLElement>() {
162
+ const ref = useRef<T>(null);
163
+
164
+ useEffect(() => {
165
+ const element = ref.current;
166
+ if (!element) return;
167
+
168
+ gsap.fromTo(
169
+ element,
170
+ { opacity: 0, x: 50 },
171
+ {
172
+ opacity: 1,
173
+ x: 0,
174
+ duration: 0.6,
175
+ ease: "power3.out",
176
+ scrollTrigger: {
177
+ trigger: element,
178
+ start: "top 85%",
179
+ toggleActions: "play none none none",
180
+ },
181
+ }
182
+ );
183
+
184
+ return () => {
185
+ ScrollTrigger.getAll().forEach((trigger) => {
186
+ if (trigger.trigger === element) {
187
+ trigger.kill();
188
+ }
189
+ });
190
+ };
191
+ }, []);
192
+
193
+ return ref;
194
+ }
195
+
196
+ // Hook for parallax effect
197
+ export function useParallax<T extends HTMLElement>(speed: number = 0.5) {
198
+ const ref = useRef<T>(null);
199
+
200
+ useEffect(() => {
201
+ const element = ref.current;
202
+ if (!element) return;
203
+
204
+ gsap.to(element, {
205
+ yPercent: speed * 100,
206
+ ease: "none",
207
+ scrollTrigger: {
208
+ trigger: element,
209
+ start: "top bottom",
210
+ end: "bottom top",
211
+ scrub: true,
212
+ },
213
+ });
214
+
215
+ return () => {
216
+ ScrollTrigger.getAll().forEach((trigger) => {
217
+ if (trigger.trigger === element) {
218
+ trigger.kill();
219
+ }
220
+ });
221
+ };
222
+ }, [speed]);
223
+
224
+ return ref;
225
+ }
226
+
227
+ // Hook for text reveal animation
228
+ export function useTextReveal<T extends HTMLElement>(delay: number = 0) {
229
+ const ref = useRef<T>(null);
230
+
231
+ useEffect(() => {
232
+ const element = ref.current;
233
+ if (!element) return;
234
+
235
+ gsap.fromTo(
236
+ element,
237
+ { opacity: 0, y: 20 },
238
+ {
239
+ opacity: 1,
240
+ y: 0,
241
+ duration: 0.6,
242
+ delay,
243
+ ease: "power3.out",
244
+ }
245
+ );
246
+ }, [delay]);
247
+
248
+ return ref;
249
+ }
250
+
251
+ // Hook for magnetic effect on hover
252
+ export function useMagnetic<T extends HTMLElement>() {
253
+ const ref = useRef<T>(null);
254
+
255
+ useEffect(() => {
256
+ const element = ref.current;
257
+ if (!element) return;
258
+
259
+ const handleMouseMove = (e: MouseEvent) => {
260
+ const rect = element.getBoundingClientRect();
261
+ const x = e.clientX - rect.left - rect.width / 2;
262
+ const y = e.clientY - rect.top - rect.height / 2;
263
+
264
+ gsap.to(element, {
265
+ x: x * 0.3,
266
+ y: y * 0.3,
267
+ duration: 0.3,
268
+ ease: "power2.out",
269
+ });
270
+ };
271
+
272
+ const handleMouseLeave = () => {
273
+ gsap.to(element, {
274
+ x: 0,
275
+ y: 0,
276
+ duration: 0.5,
277
+ ease: "elastic.out(1, 0.3)",
278
+ });
279
+ };
280
+
281
+ element.addEventListener("mousemove", handleMouseMove);
282
+ element.addEventListener("mouseleave", handleMouseLeave);
283
+
284
+ return () => {
285
+ element.removeEventListener("mousemove", handleMouseMove);
286
+ element.removeEventListener("mouseleave", handleMouseLeave);
287
+ };
288
+ }, []);
289
+
290
+ return ref;
291
+ }
292
+
293
+ // Hook for floating animation
294
+ export function useFloating<T extends HTMLElement>(
295
+ duration: number = 3,
296
+ distance: number = 10
297
+ ) {
298
+ const ref = useRef<T>(null);
299
+
300
+ useEffect(() => {
301
+ const element = ref.current;
302
+ if (!element) return;
303
+
304
+ gsap.to(element, {
305
+ y: distance,
306
+ duration: duration,
307
+ ease: "power1.inOut",
308
+ yoyo: true,
309
+ repeat: -1,
310
+ });
311
+ }, [duration, distance]);
312
+
313
+ return ref;
314
+ }
315
+
316
+ // Hook for number counter animation
317
+ export function useCounter(
318
+ end: number,
319
+ duration: number = 2,
320
+ start: number = 0
321
+ ) {
322
+ const ref = useRef<HTMLSpanElement>(null);
323
+
324
+ useEffect(() => {
325
+ const element = ref.current;
326
+ if (!element) return;
327
+
328
+ const obj = { value: start };
329
+ gsap.to(obj, {
330
+ value: end,
331
+ duration,
332
+ ease: "power2.out",
333
+ onUpdate: () => {
334
+ element.textContent = Math.round(obj.value).toString();
335
+ },
336
+ });
337
+ }, [end, duration, start]);
338
+
339
+ return ref;
340
+ }
341
+
342
+ // Hook for horizontal scroll animation
343
+ export function useHorizontalScroll<T extends HTMLElement>() {
344
+ const containerRef = useRef<T>(null);
345
+
346
+ useEffect(() => {
347
+ const container = containerRef.current;
348
+ if (!container) return;
349
+
350
+ const scrollWidth = container.scrollWidth - window.innerWidth;
351
+
352
+ gsap.to(container, {
353
+ x: -scrollWidth,
354
+ ease: "none",
355
+ scrollTrigger: {
356
+ trigger: container,
357
+ start: "top top",
358
+ end: () => `+=${scrollWidth}`,
359
+ scrub: 1,
360
+ pin: true,
361
+ anticipatePin: 1,
362
+ },
363
+ });
364
+
365
+ return () => {
366
+ ScrollTrigger.getAll().forEach((trigger) => {
367
+ if (trigger.trigger === container) {
368
+ trigger.kill();
369
+ }
370
+ });
371
+ };
372
+ }, []);
373
+
374
+ return containerRef;
375
+ }
376
+
377
+ // Hook for draw SVG animation
378
+ export function useDrawSVG<T extends SVGSVGElement>() {
379
+ const ref = useRef<T>(null);
380
+
381
+ useEffect(() => {
382
+ const element = ref.current;
383
+ if (!element) return;
384
+
385
+ const paths = element.querySelectorAll("path, line, circle, rect");
386
+
387
+ paths.forEach((path) => {
388
+ const length = (path as SVGGeometryElement).getTotalLength?.() || 100;
389
+ gsap.set(path, {
390
+ strokeDasharray: length,
391
+ strokeDashoffset: length,
392
+ });
393
+
394
+ gsap.to(path, {
395
+ strokeDashoffset: 0,
396
+ duration: 2,
397
+ ease: "power2.inOut",
398
+ scrollTrigger: {
399
+ trigger: element,
400
+ start: "top 80%",
401
+ toggleActions: "play none none none",
402
+ },
403
+ });
404
+ });
405
+
406
+ return () => {
407
+ ScrollTrigger.getAll().forEach((trigger) => {
408
+ if (trigger.trigger === element) {
409
+ trigger.kill();
410
+ }
411
+ });
412
+ };
413
+ }, []);
414
+
415
+ return ref;
416
+ }
417
+
418
+ // Utility to kill all scroll triggers (use on route change)
419
+ export function killAllScrollTriggers() {
420
+ ScrollTrigger.getAll().forEach((trigger) => trigger.kill());
421
+ }
422
+
423
+ // Refresh scroll trigger (call after layout changes)
424
+ export function refreshScrollTrigger() {
425
+ ScrollTrigger.refresh();
426
+ }
package/lib/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
package/next-env.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+ import "./.next/types/routes.d.ts";
4
+
5
+ // NOTE: This file should not be edited
6
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
@@ -0,0 +1,6 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ };
5
+
6
+ export default nextConfig;
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "gradient-forge",
3
+ "version": "1.0.0",
4
+ "description": "A production-ready gradient theming framework for shadcn/ui with CLI support",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "gradient-forge": "./cli/index.mjs"
9
+ },
10
+ "scripts": {
11
+ "dev": "next dev --turbopack",
12
+ "build": "next build",
13
+ "start": "next start",
14
+ "lint": "next lint"
15
+ },
16
+ "keywords": [
17
+ "gradient",
18
+ "theme",
19
+ "shadcn",
20
+ "theming",
21
+ "ui",
22
+ "css",
23
+ "tailwind"
24
+ ],
25
+ "author": "",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/karanjadon648/shadcn-gradient-forge"
30
+ },
31
+ "dependencies": {
32
+ "@gsap/react": "^2.1.2",
33
+ "clsx": "^2.1.1",
34
+ "framer-motion": "^12.34.0",
35
+ "gsap": "^3.14.2",
36
+ "lucide-react": "^0.525.0",
37
+ "react": "19.1.0",
38
+ "react-dom": "19.1.0",
39
+ "tailwind-merge": "^2.5.5"
40
+ },
41
+ "devDependencies": {
42
+ "@tailwindcss/postcss": "^4.1.4",
43
+ "@types/node": "^24.0.10",
44
+ "@types/react": "19.1.8",
45
+ "@types/react-dom": "19.1.6",
46
+ "eslint": "^9.39.2",
47
+ "eslint-config-next": "^16.1.6",
48
+ "next": "^16.1.6",
49
+ "postcss": "^8",
50
+ "tailwindcss": "^4.1.4",
51
+ "typescript": "^5.8.3"
52
+ }
53
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
@@ -0,0 +1,15 @@
1
+ import type { Config } from "tailwindcss";
2
+
3
+ const config: Config = {
4
+ content: [
5
+ "./app/**/*.{ts,tsx}",
6
+ "./components/**/*.{ts,tsx}",
7
+ "./lib/**/*.{ts,tsx}",
8
+ ],
9
+ theme: {
10
+ extend: {},
11
+ },
12
+ plugins: [],
13
+ };
14
+
15
+ export default config;
package/tsconfig.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": [
5
+ "dom",
6
+ "dom.iterable",
7
+ "esnext"
8
+ ],
9
+ "allowJs": true,
10
+ "skipLibCheck": true,
11
+ "strict": true,
12
+ "noEmit": true,
13
+ "esModuleInterop": true,
14
+ "module": "esnext",
15
+ "moduleResolution": "bundler",
16
+ "resolveJsonModule": true,
17
+ "isolatedModules": true,
18
+ "jsx": "react-jsx",
19
+ "incremental": true,
20
+ "paths": {
21
+ "@/*": [
22
+ "./*"
23
+ ]
24
+ },
25
+ "plugins": [
26
+ {
27
+ "name": "next"
28
+ }
29
+ ]
30
+ },
31
+ "include": [
32
+ "**/*.ts",
33
+ "**/*.tsx",
34
+ "next-env.d.ts",
35
+ ".next/types/**/*.ts",
36
+ ".next/dev/types/**/*.ts"
37
+ ],
38
+ "exclude": [
39
+ "node_modules",
40
+ "cli/templates/**/*",
41
+ "gradient-forge/**/*"
42
+ ]
43
+ }