apex-ui-kit 2.0.5

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 (40) hide show
  1. package/Licence.md +21 -0
  2. package/ReadMe.md +75 -0
  3. package/dist/ApexUI-Kit/templates/Avatar/Avatar.jsx +81 -0
  4. package/dist/ApexUI-Kit/templates/BeamBar/BeamBar.jsx +101 -0
  5. package/dist/ApexUI-Kit/templates/Cards/BasicCard.jsx +60 -0
  6. package/dist/ApexUI-Kit/templates/Carousel/Carousel.jsx +95 -0
  7. package/dist/ApexUI-Kit/templates/Carousel/DragCarousel.jsx +105 -0
  8. package/dist/ApexUI-Kit/templates/Carousel/ParallaxCarousel.jsx +161 -0
  9. package/dist/ApexUI-Kit/templates/DragCarousel/DragCarousel.jsx +105 -0
  10. package/dist/ApexUI-Kit/templates/EdgeGlowForm/EdgeGlowFrom.jsx +297 -0
  11. package/dist/ApexUI-Kit/templates/FlipProfile/FlipProfile.jsx +125 -0
  12. package/dist/ApexUI-Kit/templates/GlareCard/GlareCard.jsx +146 -0
  13. package/dist/ApexUI-Kit/templates/GlassCardStack/GlassCardStack.jsx +111 -0
  14. package/dist/ApexUI-Kit/templates/HoverText/HoverText.jsx +371 -0
  15. package/dist/ApexUI-Kit/templates/HyperCard/HyperCard.jsx +183 -0
  16. package/dist/ApexUI-Kit/templates/KineticThreadsBackground/KineticThreadBackground.jsx +249 -0
  17. package/dist/ApexUI-Kit/templates/LensFlareBackground/LensFlareBackground.jsx +268 -0
  18. package/dist/ApexUI-Kit/templates/LuminousParticleOcean/LuminousParticleOcean.jsx +155 -0
  19. package/dist/ApexUI-Kit/templates/NavDropDown/NavDropDown.jsx +114 -0
  20. package/dist/ApexUI-Kit/templates/NavMenu/NavMenu.jsx +153 -0
  21. package/dist/ApexUI-Kit/templates/OrbsBackground/FloatingOrbs.jsx +96 -0
  22. package/dist/ApexUI-Kit/templates/ParallaxCard/ParallaxCard.jsx +127 -0
  23. package/dist/ApexUI-Kit/templates/ParallaxCarousel/ParallaxCarousel.jsx +161 -0
  24. package/dist/ApexUI-Kit/templates/ParallaxProfile/ParallaxProfile.jsx +107 -0
  25. package/dist/ApexUI-Kit/templates/PointerFollower/PointerFollower.jsx +126 -0
  26. package/dist/ApexUI-Kit/templates/RainBackground/RainBackground.jsx +109 -0
  27. package/dist/ApexUI-Kit/templates/RippleBackground/RippleBackground.jsx +160 -0
  28. package/dist/ApexUI-Kit/templates/ScrollMarquee/ScrollMarquee.jsx +174 -0
  29. package/dist/ApexUI-Kit/templates/SmartGridCard/SmartGridCard.jsx +241 -0
  30. package/dist/ApexUI-Kit/templates/SmokeBackground/SmokeBackground.jsx +216 -0
  31. package/dist/ApexUI-Kit/templates/Testimonials/Testimonials.jsx +117 -0
  32. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeAnimation.js +252 -0
  33. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeConfig.js +8 -0
  34. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.css +560 -0
  35. package/dist/ApexUI-Kit/templates/ThemeToggle/ThemeToggle.jsx +118 -0
  36. package/dist/ApexUI-Kit/templates/ToolTip/ToolTip.jsx +85 -0
  37. package/dist/ApexUI-Kit/templates/WaterDropReveal/WaterDropReveal.jsx +151 -0
  38. package/dist/cli/index.d.ts +2 -0
  39. package/dist/cli/index.js +145 -0
  40. package/package.json +56 -0
@@ -0,0 +1,371 @@
1
+ import React, { useRef } from "react";
2
+ import gsap from "gsap";
3
+
4
+ const HoverText = ({
5
+ text = "ApexUI",
6
+ fontSize = "2.5rem",
7
+ className = "",
8
+ effect, // if not passed, use 'defaultReveal'
9
+ effectColor = "#C27AFF"
10
+ }) => {
11
+ const charsRef = useRef([]);
12
+ const containerRef = useRef(null);
13
+
14
+ // Default animated reveal
15
+ function handleDefaultEnter(i) {
16
+ if (effect && effect !== "defaultReveal") return;
17
+ const el = charsRef.current[i];
18
+ if (!el) return;
19
+ gsap.to(el, {
20
+ scale: 1.4,
21
+ rotate: gsap.utils.random(-10, 10),
22
+ color: effectColor,
23
+ filter: `drop-shadow(0 4px 32px ${effectColor})`,
24
+ duration: 0.32,
25
+ ease: "elastic.out(1,0.5)",
26
+ });
27
+ }
28
+ function handleDefaultLeave(i) {
29
+ if (effect && effect !== "defaultReveal") return;
30
+ const el = charsRef.current[i];
31
+ if (!el) return;
32
+ gsap.to(el, {
33
+ scale: 1,
34
+ rotate: 0,
35
+ color: "#fff",
36
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
37
+ duration: 0.32,
38
+ ease: "expo.inOut",
39
+ });
40
+ }
41
+
42
+ // Party effect
43
+ function handlePartyEnter() {
44
+ if (effect !== "party") return;
45
+ charsRef.current.forEach((el, i) => {
46
+ if (!el) return;
47
+ gsap.to(el, {
48
+ scale: 1.25,
49
+ rotate: (i % 2 === 0 ? 1 : -1) * (18 + i * 4),
50
+ color: effectColor,
51
+ filter: `drop-shadow(0 4px 32px ${effectColor})`,
52
+ duration: 0.38,
53
+ ease: "elastic.out(1,0.5)",
54
+ });
55
+ });
56
+ }
57
+ function handlePartyLeave() {
58
+ if (effect !== "party") return;
59
+ charsRef.current.forEach((el) => {
60
+ if (!el) return;
61
+ gsap.to(el, {
62
+ scale: 1,
63
+ rotate: 0,
64
+ color: "#fff",
65
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
66
+ duration: 0.38,
67
+ ease: "expo.inOut",
68
+ });
69
+ });
70
+ }
71
+
72
+ // Reset all letters on mount or color change
73
+ React.useEffect(() => {
74
+ charsRef.current.forEach((el) => {
75
+ if (el) {
76
+ gsap.set(el, {
77
+ scale: 1,
78
+ rotate: 0,
79
+ x: 0,
80
+ y: 0,
81
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
82
+ color: "#fff",
83
+ });
84
+ }
85
+ });
86
+ }, [text, effectColor]);
87
+
88
+ // Magnetic effect
89
+ const handleMouseMove = (e) => {
90
+ if (effect !== "magnetic") return;
91
+ const rect = containerRef.current.getBoundingClientRect();
92
+ const mouseX = e.clientX - rect.left;
93
+ const mouseY = e.clientY - rect.top;
94
+ let minDist = Infinity;
95
+ let minIdx = -1;
96
+ charsRef.current.forEach((el, i) => {
97
+ if (!el) return;
98
+ const elRect = el.getBoundingClientRect();
99
+ const elCenterX = elRect.left - rect.left + elRect.width / 2;
100
+ const elCenterY = elRect.top - rect.top + elRect.height / 2;
101
+ const dx = (mouseX - elCenterX) / rect.width;
102
+ const dy = (mouseY - elCenterY) / rect.height;
103
+ const dist = Math.sqrt(dx * dx + dy * dy);
104
+ if (dist < minDist) {
105
+ minDist = dist;
106
+ minIdx = i;
107
+ }
108
+ });
109
+ // Animate all letters
110
+ charsRef.current.forEach((el, i) => {
111
+ if (!el) return;
112
+ const elRect = el.getBoundingClientRect();
113
+ const elCenterX = elRect.left - rect.left + elRect.width / 2;
114
+ const elCenterY = elRect.top - rect.top + elRect.height / 2;
115
+ const dx = (mouseX - elCenterX) / rect.width;
116
+ const dy = (mouseY - elCenterY) / rect.height;
117
+ const dist = Math.sqrt(dx * dx + dy * dy);
118
+ const maxDist = 0.35;
119
+ const mag = Math.max(0, 1 - dist / maxDist);
120
+ const isFocus = i === minIdx;
121
+ gsap.to(el, {
122
+ x: dx * 18 * mag,
123
+ y: dy * 18 * mag,
124
+ scale: isFocus ? 1.22 : 1 + 0.12 * mag,
125
+ rotate: dx * 10 * mag,
126
+ filter: isFocus
127
+ ? `drop-shadow(0 4px 32px ${effectColor})`
128
+ : `drop-shadow(0 2px 16px ${effectColor}44)`,
129
+ color: isFocus ? effectColor : "#fff",
130
+ zIndex: isFocus ? 10 : 2,
131
+ duration: 0.35,
132
+ ease: "expo.out",
133
+ });
134
+ });
135
+ };
136
+
137
+ // Rubber effect
138
+ function handleRubberEnter(i) {
139
+ if (effect !== "rubber") return;
140
+ const el = charsRef.current[i];
141
+ if (!el) return;
142
+ gsap.to(el, {
143
+ scaleX: 1.5,
144
+ scaleY: 0.7,
145
+ color: effectColor,
146
+ filter: `drop-shadow(0 4px 32px ${effectColor})`,
147
+ duration: 0.22,
148
+ ease: "expo.out",
149
+ onComplete: () => {
150
+ gsap.to(el, {
151
+ scaleX: 0.85,
152
+ scaleY: 1.18,
153
+ duration: 0.18,
154
+ ease: "expo.inOut",
155
+ onComplete: () => {
156
+ gsap.to(el, {
157
+ scaleX: 1,
158
+ scaleY: 1,
159
+ color: "#fff",
160
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
161
+ duration: 0.18,
162
+ ease: "expo.inOut",
163
+ });
164
+ }
165
+ });
166
+ }
167
+ });
168
+ }
169
+
170
+ // Jump effect
171
+ function handleJumpEnter(i) {
172
+ if (effect !== "jump") return;
173
+ for (let j = -1; j <= 1; j++) {
174
+ const idx = i + j;
175
+ const el = charsRef.current[idx];
176
+ if (!el) continue;
177
+ if (j === 0) {
178
+ gsap.to(el, {
179
+ y: -28,
180
+ color: effectColor,
181
+ filter: `drop-shadow(0 4px 32px ${effectColor})`,
182
+ duration: 0.32,
183
+ ease: "expo.out",
184
+ });
185
+ } else {
186
+ gsap.to(el, {
187
+ x: j * 8,
188
+ rotate: j * 8,
189
+ duration: 0.22,
190
+ ease: "expo.out",
191
+ });
192
+ }
193
+ }
194
+ }
195
+ function handleJumpLeave(i) {
196
+ if (effect !== "jump") return;
197
+ for (let j = -1; j <= 1; j++) {
198
+ const idx = i + j;
199
+ const el = charsRef.current[idx];
200
+ if (!el) continue;
201
+ gsap.to(el, {
202
+ x: 0,
203
+ y: 0,
204
+ scale: 1,
205
+ rotate: 0,
206
+ color: "#fff",
207
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
208
+ duration: 0.32,
209
+ ease: "expo.inOut",
210
+ });
211
+ }
212
+ }
213
+
214
+ // Rotate effect
215
+ function handleRotateEnter(i) {
216
+ if (effect !== "rotate") return;
217
+ for (let j = -1; j <= 1; j++) {
218
+ const idx = i + j;
219
+ const el = charsRef.current[idx];
220
+ if (!el) continue;
221
+ if (j === 0) {
222
+ gsap.to(el, {
223
+ rotate: 24,
224
+ scale: 1.18,
225
+ color: effectColor,
226
+ filter: `drop-shadow(0 4px 32px ${effectColor})`,
227
+ duration: 0.32,
228
+ ease: "expo.out",
229
+ });
230
+ } else {
231
+ gsap.to(el, {
232
+ rotate: j * 12,
233
+ duration: 0.22,
234
+ ease: "expo.out",
235
+ });
236
+ }
237
+ }
238
+ }
239
+ function handleRotateLeave(i) {
240
+ if (effect !== "rotate") return;
241
+ for (let j = -1; j <= 1; j++) {
242
+ const idx = i + j;
243
+ const el = charsRef.current[idx];
244
+ if (!el) continue;
245
+ gsap.to(el, {
246
+ scale: 1,
247
+ rotate: 0,
248
+ color: "#fff",
249
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
250
+ duration: 0.32,
251
+ ease: "expo.inOut",
252
+ });
253
+ }
254
+ }
255
+
256
+ // Wave effect
257
+ function handleCharEnter(i) {
258
+ if (effect !== "wave") return;
259
+ for (let j = -2; j <= 2; j++) {
260
+ const idx = i + j;
261
+ const el = charsRef.current[idx];
262
+ if (!el) continue;
263
+ gsap.to(el, {
264
+ y: -18 + Math.abs(j) * 8,
265
+ scale: 1.12 - Math.abs(j) * 0.08,
266
+ color: effectColor,
267
+ filter: `drop-shadow(0 4px 32px ${effectColor})`,
268
+ duration: 0.38 + Math.abs(j) * 0.08,
269
+ ease: "elastic.out(1,0.5)",
270
+ });
271
+ }
272
+ }
273
+ function handleCharLeave(i) {
274
+ if (effect !== "wave") return;
275
+ for (let j = -2; j <= 2; j++) {
276
+ const idx = i + j;
277
+ const el = charsRef.current[idx];
278
+ if (!el) continue;
279
+ gsap.to(el, {
280
+ y: 0,
281
+ scale: 1,
282
+ color: "#fff",
283
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
284
+ duration: 0.38 + Math.abs(j) * 0.08,
285
+ ease: "expo.inOut",
286
+ });
287
+ }
288
+ }
289
+
290
+ // Reset on mouse leave
291
+ const handleMouseLeave = () => {
292
+ if (effect === "magnetic") {
293
+ charsRef.current.forEach((el) => {
294
+ if (el) {
295
+ gsap.to(el, {
296
+ x: 0,
297
+ y: 0,
298
+ scale: 1,
299
+ rotate: 0,
300
+ color: "#fff",
301
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
302
+ duration: 0.6,
303
+ ease: "expo.inOut",
304
+ });
305
+ }
306
+ });
307
+ } else if (effect === "flip") {
308
+ charsRef.current.forEach((el) => {
309
+ if (el) {
310
+ gsap.to(el, {
311
+ rotateY: 0,
312
+ color: "#fff",
313
+ filter: `drop-shadow(0 0px 0px ${effectColor})`,
314
+ duration: 0.6,
315
+ ease: "expo.inOut",
316
+ });
317
+ }
318
+ });
319
+ }
320
+ };
321
+
322
+ return (
323
+ <div
324
+ ref={containerRef}
325
+ className={`relative inline-flex items-center select-none cursor-pointer ${className}`}
326
+ style={{ fontSize, fontWeight: 900, letterSpacing: "0.08em", textTransform: "uppercase", background: "none", userSelect: "none" }}
327
+ onMouseMove={effect === "magnetic" ? handleMouseMove : undefined}
328
+ onMouseLeave={effect === "party" ? handlePartyLeave : handleMouseLeave}
329
+ onMouseEnter={effect === "party" ? handlePartyEnter : undefined}
330
+ >
331
+ {text.split("").map((char, i) => (
332
+ <span
333
+ key={i}
334
+ ref={el => charsRef.current[i] = el}
335
+ onMouseEnter={
336
+ !effect || effect === "defaultReveal" ? () => handleDefaultEnter(i)
337
+ : effect === "wave" ? () => handleCharEnter(i)
338
+ : effect === "rubber" ? () => handleRubberEnter(i)
339
+ : effect === "jump" ? () => handleJumpEnter(i)
340
+ : effect === "rotate" ? () => handleRotateEnter(i)
341
+ : undefined
342
+ }
343
+ onMouseLeave={
344
+ !effect || effect === "defaultReveal" ? () => handleDefaultLeave(i)
345
+ : effect === "wave" ? () => handleCharLeave(i)
346
+ : effect === "rubber" ? () => handleRubberEnter(i)
347
+ : effect === "jump" ? () => handleJumpLeave(i)
348
+ : effect === "rotate" ? () => handleRotateLeave(i)
349
+ : undefined
350
+ }
351
+ style={{
352
+ display: "inline-block",
353
+ marginRight: char === " " ? "0.18em" : 0,
354
+ padding: "0 0.04em",
355
+ willChange: "transform, color, filter, opacity",
356
+ borderRadius: "0.18em",
357
+ position: "relative",
358
+ zIndex: 2,
359
+ color: "#fff",
360
+ background: "none",
361
+ transition: "filter 0.3s, color 0.3s, background 0.3s, opacity 0.3s",
362
+ }}
363
+ >
364
+ {char === " " ? "\u00A0" : char}
365
+ </span>
366
+ ))}
367
+ </div>
368
+ );
369
+ };
370
+
371
+ export default HoverText;
@@ -0,0 +1,183 @@
1
+ import React, { useRef, useEffect, useState } from "react";
2
+ import { gsap } from "gsap";
3
+ import {
4
+ faCube,
5
+ faChevronRight, // Changed icon for a better look with the new button
6
+ } from "@fortawesome/free-solid-svg-icons";
7
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
8
+
9
+ const HyperCard = ({
10
+ // Content props remain unchanged
11
+ text = "Introducing Hyper Card",
12
+ LastText = "Effect",
13
+ LastTextColor = "#9AE600", // Green color from your original code
14
+ SubText = "Fully animated lightning effect card with customizable props, built with Apex UI.",
15
+ title = "Apex UI Hyper Card",
16
+ description = "Experience futuristic animations with customizable props like glow, beam, star count, and hover interactions.",
17
+ buttonText = "Get Started",
18
+
19
+ // Animation props remain unchanged
20
+ starColor = "#9AE600", // Green stars
21
+ starCount = 250,
22
+ glow = true,
23
+ baseSpeed = 0.5,
24
+ warpSpeed = 8.5,
25
+ circlePosition = "bottom",
26
+ }) => {
27
+ const canvasRef = useRef(null);
28
+ const contentRef = useRef(null);
29
+ const [hovered, setHovered] = useState(false);
30
+ const stars = useRef([]);
31
+ const speedRef = useRef({ value: baseSpeed });
32
+
33
+ // 🎇 Particle logic remains exactly the same
34
+ useEffect(() => {
35
+ const canvas = canvasRef.current;
36
+ const ctx = canvas.getContext("2d");
37
+ let animationFrame;
38
+ let w, h;
39
+
40
+ const init = () => {
41
+ w = canvas.width = canvas.offsetWidth;
42
+ h = canvas.height = canvas.offsetHeight;
43
+ stars.current = Array.from({ length: starCount }, () => {
44
+ const z = Math.random() * w;
45
+ return {
46
+ x: Math.random() * w, y: Math.random() * h,
47
+ z, baseZ: z, brightness: Math.random() * 0.5 + 0.5,
48
+ };
49
+ });
50
+ };
51
+
52
+ const draw = () => {
53
+ // Updated to draw on a transparent background
54
+ if (!ctx) return;
55
+ ctx.clearRect(0, 0, w, h);
56
+ ctx.fillStyle = starColor;
57
+
58
+ stars.current.forEach((star) => {
59
+ star.z -= speedRef.current.value;
60
+ if (star.z <= 1) star.z = star.baseZ;
61
+ const sx = (star.x - w / 2) * (w / star.z) + w / 2;
62
+ const sy = (star.y - h / 2) * (h / star.z) + h / 2;
63
+ const size = (1 - star.z / w) * 2;
64
+ ctx.beginPath();
65
+ ctx.globalAlpha = star.brightness;
66
+ if (speedRef.current.value > baseSpeed + 0.3) {
67
+ const tailLength = speedRef.current.value * 3;
68
+ const dx = sx - w / 2, dy = sy - h / 2;
69
+ const mag = Math.sqrt(dx * dx + dy * dy) || 1;
70
+ ctx.moveTo(sx, sy);
71
+ ctx.lineTo(sx + (dx / mag) * tailLength, sy + (dy / mag) * tailLength);
72
+ ctx.strokeStyle = starColor;
73
+ ctx.lineWidth = 1;
74
+ ctx.stroke();
75
+ } else {
76
+ ctx.arc(sx, sy, size > 0 ? size : 0.1, 0, Math.PI * 2);
77
+ ctx.fill();
78
+ }
79
+ ctx.globalAlpha = 1;
80
+ });
81
+
82
+ animationFrame = requestAnimationFrame(draw);
83
+ };
84
+
85
+ init();
86
+ draw();
87
+ window.addEventListener("resize", init);
88
+ return () => { cancelAnimationFrame(animationFrame); window.removeEventListener("resize", init); };
89
+ }, [starColor, starCount, baseSpeed]);
90
+
91
+ // GSAP animations for speed and jiggle remain the same
92
+ useEffect(() => {
93
+ gsap.to(speedRef.current, { value: hovered ? warpSpeed : baseSpeed, duration: 1.5, ease: "power4.inOut" });
94
+ }, [hovered, warpSpeed, baseSpeed]);
95
+
96
+ useEffect(() => {
97
+ const el = contentRef.current;
98
+ if (hovered) {
99
+ gsap.to(el, { scale: 0.95, duration: 0.5, ease: "power1.out" });
100
+ gsap.to(el, { x: "+=1", y: "-=1", duration: 0.05, repeat: -1, yoyo: true, ease: "sine.inOut" });
101
+ } else {
102
+ gsap.killTweensOf(el);
103
+ gsap.to(el, { x: 0, y: 0, scale: 1, duration: 0.4, ease: "power2.out" });
104
+ }
105
+ }, [hovered]);
106
+
107
+ return (
108
+ <div
109
+ className="w-full max-w-xs mx-auto rounded-2xl overflow-hidden text-white
110
+ border border-slate-700 bg-slate-900 shadow-2xl shadow-black/50"
111
+ style={{
112
+ backgroundImage: 'linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px)',
113
+ backgroundSize: '2rem 2rem'
114
+ }}
115
+ >
116
+ {/* Top animated HyperCard */}
117
+ <div
118
+ className="relative w-full h-50 overflow-hidden group cursor-pointer"
119
+ onMouseEnter={() => setHovered(true)}
120
+ onMouseLeave={() => setHovered(false)}
121
+ >
122
+ <canvas ref={canvasRef} className="absolute inset-0 w-full h-full z-0" />
123
+
124
+ {/* Glow effect from your original code */}
125
+ {glow && (
126
+ <div
127
+ className="absolute inset-0 w-full h-full z-10 pointer-events-none"
128
+ style={{
129
+ mixBlendMode: "soft-light",
130
+ opacity: 0.8,
131
+ background: `radial-gradient(circle at ${circlePosition}, ${starColor} 0%, transparent 70%)`,
132
+ }}
133
+ />
134
+ )}
135
+
136
+ {/* Glowing separator line */}
137
+ <div className="absolute bottom-0 w-full h-[2px] bg-purple-500/50 blur-sm"></div>
138
+
139
+ <div ref={contentRef} className="relative z-20 flex flex-col items-center justify-center w-full h-full px-4 py-2">
140
+ <h2 className="text-lg sm:text-xl font-normal text-center text-white">
141
+ {text}{" "}
142
+ {/* Fixed color issue by using inline style */}
143
+ <span className="font-bold ml-1" style={{ color: LastTextColor }}>
144
+ {LastText}
145
+ </span>
146
+ </h2>
147
+ <p className="text-sm text-gray-300 text-center max-w-sm mt-1">{SubText}</p>
148
+ </div>
149
+ </div>
150
+
151
+ {/* Bottom content section */}
152
+ <div className="px-6 pt-5 pb-6 w-full text-left flex flex-col items-start">
153
+ <div className="flex items-center gap-3 mb-2">
154
+ <FontAwesomeIcon icon={faCube} style={{ color: starColor }} className="text-2xl" />
155
+ <h3 className="text-2xl font-extrabold text-white tracking-tight">{title}</h3>
156
+ </div>
157
+ <p className="text-gray-400 text-base mb-5 max-w-xs leading-relaxed">{description}</p>
158
+
159
+ {/* --- NEW BUTTON WITH ANIMATION --- */}
160
+ <button
161
+ className="group relative flex items-center gap-2 px-6 py-2.5
162
+ border-2 border-[#9AE600] rounded-lg
163
+ font-semibold text-white overflow-hidden transition-all duration-300"
164
+ >
165
+ {/* Fill-up background effect */}
166
+ <span
167
+ className="absolute inset-0 h-full w-0 bg-[#4c6d0a]
168
+ transition-all duration-300 ease-in-out group-hover:w-full"
169
+ style={{ transformOrigin: 'bottom' }}
170
+ ></span>
171
+
172
+ {/* Button text and icon */}
173
+ <span className="relative z-10 flex items-center gap-2">
174
+ {buttonText}
175
+ <FontAwesomeIcon icon={faChevronRight} className="transition-transform duration-300 group-hover:translate-x-1" />
176
+ </span>
177
+ </button>
178
+ </div>
179
+ </div>
180
+ );
181
+ };
182
+
183
+ export default HyperCard;