Florin Bobiș commited on
Commit
da36ada
·
1 Parent(s): e1becaa
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .eslintrc.json +3 -0
  2. .gitignore +38 -0
  3. Dockerfile +23 -0
  4. components.json +17 -0
  5. next.config.mjs +4 -0
  6. package-lock.json +0 -0
  7. package.json +37 -0
  8. postcss.config.mjs +8 -0
  9. public/images/1.png +0 -0
  10. public/images/10.png +0 -0
  11. public/images/2.png +0 -0
  12. public/images/3.png +0 -0
  13. public/images/4.png +0 -0
  14. public/images/5.png +0 -0
  15. public/images/6.png +0 -0
  16. public/images/7.png +0 -0
  17. public/images/8.png +0 -0
  18. public/images/9.png +0 -0
  19. public/images/client1.webp +0 -0
  20. public/images/client10.webp +0 -0
  21. public/images/client11.webp +0 -0
  22. public/images/client12.webp +0 -0
  23. public/images/client13.webp +0 -0
  24. public/images/client2.webp +0 -0
  25. public/images/client3.webp +0 -0
  26. public/images/client4.webp +0 -0
  27. public/images/client5.webp +0 -0
  28. public/images/client6.webp +0 -0
  29. public/images/client7.webp +0 -0
  30. public/images/client8.webp +0 -0
  31. public/images/client9.webp +0 -0
  32. public/images/p1.png +0 -0
  33. public/images/p2.png +0 -0
  34. public/images/p3.png +0 -0
  35. public/images/p4.png +0 -0
  36. public/images/temp-banner.png +0 -0
  37. public/logo-dark.png +0 -0
  38. public/logo-transparent.png +0 -0
  39. public/logo.png +0 -0
  40. src/app/favicon.ico +0 -0
  41. src/app/globals.css +76 -0
  42. src/app/layout.tsx +33 -0
  43. src/app/page.tsx +194 -0
  44. src/components/global/navbar.tsx +41 -0
  45. src/components/ui/3d-card.tsx +151 -0
  46. src/components/ui/button.tsx +56 -0
  47. src/components/ui/card.tsx +79 -0
  48. src/components/ui/container-scroll-animation.tsx +103 -0
  49. src/components/ui/hero-parallax.tsx +159 -0
  50. src/components/ui/infinite-moving-cards.tsx +102 -0
.eslintrc.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "extends": "next/core-web-vitals"
3
+ }
.gitignore ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+ .yarn/install-state.gz
8
+
9
+ # testing
10
+ /coverage
11
+
12
+ # next.js
13
+ /.next/
14
+ /out/
15
+
16
+ # production
17
+ /build
18
+
19
+ # misc
20
+ .DS_Store
21
+ *.pem
22
+
23
+ # debug
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ yarn-error.log*
27
+
28
+ # local env files
29
+ .env*.local
30
+
31
+ # vercel
32
+ .vercel
33
+
34
+ # typescript
35
+ *.tsbuildinfo
36
+ next-env.d.ts
37
+
38
+ certificates
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Node.js runtime as the base image
2
+ FROM node:18-alpine
3
+
4
+ # Set the working directory in the container
5
+ WORKDIR /app
6
+
7
+ # Copy package.json and package-lock.json to the working directory
8
+ COPY package*.json ./
9
+
10
+ # Install project dependencies
11
+ RUN npm install
12
+
13
+ # Copy all source files to the working directory
14
+ COPY . .
15
+
16
+ # Build the Next.js application
17
+ RUN npm run build
18
+
19
+ # Expose the port your app runs on
20
+ EXPOSE 7860
21
+
22
+ # Define the command to run your app
23
+ CMD [ "npm", "start" ]
components.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "default",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.ts",
8
+ "css": "src/app/globals.css",
9
+ "baseColor": "slate",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils"
16
+ }
17
+ }
next.config.mjs ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {};
3
+
4
+ export default nextConfig;
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "atom-landing",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev --experimental-https",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@radix-ui/react-slot": "^1.1.0",
13
+ "@tsparticles/engine": "^3.5.0",
14
+ "@tsparticles/react": "^3.0.0",
15
+ "@tsparticles/slim": "^3.5.0",
16
+ "class-variance-authority": "^0.7.0",
17
+ "clsx": "^2.1.1",
18
+ "framer-motion": "^11.3.31",
19
+ "lucide-react": "^0.436.0",
20
+ "next": "14.2.7",
21
+ "next-themes": "^0.3.0",
22
+ "react": "^18",
23
+ "react-dom": "^18",
24
+ "tailwind-merge": "^2.5.2",
25
+ "tailwindcss-animate": "^1.0.7"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20",
29
+ "@types/react": "^18",
30
+ "@types/react-dom": "^18",
31
+ "eslint": "^8",
32
+ "eslint-config-next": "14.2.7",
33
+ "postcss": "^8",
34
+ "tailwindcss": "^3.4.1",
35
+ "typescript": "^5"
36
+ }
37
+ }
postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ tailwindcss: {},
5
+ },
6
+ };
7
+
8
+ export default config;
public/images/1.png ADDED
public/images/10.png ADDED
public/images/2.png ADDED
public/images/3.png ADDED
public/images/4.png ADDED
public/images/5.png ADDED
public/images/6.png ADDED
public/images/7.png ADDED
public/images/8.png ADDED
public/images/9.png ADDED
public/images/client1.webp ADDED
public/images/client10.webp ADDED
public/images/client11.webp ADDED
public/images/client12.webp ADDED
public/images/client13.webp ADDED
public/images/client2.webp ADDED
public/images/client3.webp ADDED
public/images/client4.webp ADDED
public/images/client5.webp ADDED
public/images/client6.webp ADDED
public/images/client7.webp ADDED
public/images/client8.webp ADDED
public/images/client9.webp ADDED
public/images/p1.png ADDED
public/images/p2.png ADDED
public/images/p3.png ADDED
public/images/p4.png ADDED
public/images/temp-banner.png ADDED
public/logo-dark.png ADDED
public/logo-transparent.png ADDED
public/logo.png ADDED
src/app/favicon.ico ADDED
src/app/globals.css ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ *,
6
+ *::before,
7
+ *::after {
8
+ box-sizing: border-box;
9
+ }
10
+
11
+ *::-webkit-scrollbar {
12
+ display: none !important;
13
+ }
14
+ .bg-radial-gradient {
15
+ background-image: radial-gradient(
16
+ circle at 10% 20%,
17
+ rgba(4, 159, 108, 1) 0%,
18
+ rgba(194, 254, 113, 1) 90.1%
19
+ );
20
+ }
21
+
22
+ @layer base {
23
+ :root {
24
+ --background: 0 0% 100%;
25
+ --foreground: 0 0% 3.9%;
26
+ --card: 0 0% 100%;
27
+ --card-foreground: 0 0% 3.9%;
28
+ --popover: 0 0% 100%;
29
+ --popover-foreground: 0 0% 3.9%;
30
+ --primary: 0 0% 9%;
31
+ --primary-foreground: 0 0% 98%;
32
+ --secondary: 0 0% 96.1%;
33
+ --secondary-foreground: 0 0% 9%;
34
+ --muted: 0 0% 96.1%;
35
+ --muted-foreground: 0 0% 45.1%;
36
+ --accent: 0 0% 96.1%;
37
+ --accent-foreground: 0 0% 9%;
38
+ --destructive: 0 84.2% 60.2%;
39
+ --destructive-foreground: 0 0% 98%;
40
+ --border: 0 0% 89.8%;
41
+ --input: 0 0% 89.8%;
42
+ --ring: 0 0% 3.9%;
43
+ --radius: 0.5rem;
44
+ }
45
+
46
+ .dark {
47
+ --background: 0 0% 3.9%;
48
+ --foreground: 0 0% 98%;
49
+ --card: 0 0% 3.9%;
50
+ --card-foreground: 0 0% 98%;
51
+ --popover: 0 0% 3.9%;
52
+ --popover-foreground: 0 0% 98%;
53
+ --primary: 0 0% 98%;
54
+ --primary-foreground: 0 0% 9%;
55
+ --secondary: 0 0% 14.9%;
56
+ --secondary-foreground: 0 0% 98%;
57
+ --muted: 0 0% 14.9%;
58
+ --muted-foreground: 0 0% 63.9%;
59
+ --accent: 0 0% 14.9%;
60
+ --accent-foreground: 0 0% 98%;
61
+ --destructive: 0 62.8% 30.6%;
62
+ --destructive-foreground: 0 0% 98%;
63
+ --border: 0 0% 14.9%;
64
+ --input: 0 0% 14.9%;
65
+ --ring: 0 0% 83.1%;
66
+ }
67
+ }
68
+
69
+ @layer base {
70
+ * {
71
+ @apply border-border;
72
+ }
73
+ body {
74
+ @apply bg-background text-foreground;
75
+ }
76
+ }
src/app/layout.tsx ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Metadata } from "next";
2
+ import { DM_Sans, Inter } from "next/font/google";
3
+ import "./globals.css";
4
+ import { ThemeProvider } from "next-themes";
5
+
6
+ const inter = Inter({ subsets: ["latin"] });
7
+ const dm_sans = DM_Sans({ subsets: ["latin"] });
8
+
9
+ export const metadata: Metadata = {
10
+ title: "G-Space Atom",
11
+ description: "Your One-stop Shop For Gravity-free Design",
12
+ };
13
+
14
+ export default function RootLayout({
15
+ children,
16
+ }: Readonly<{
17
+ children: React.ReactNode;
18
+ }>) {
19
+ return (
20
+ <html lang="en">
21
+ <body className={dm_sans.className}>
22
+ <ThemeProvider
23
+ attribute="class"
24
+ defaultTheme="dark"
25
+ enableSystem
26
+ disableTransitionOnChange
27
+ >
28
+ {children}
29
+ </ThemeProvider>
30
+ </body>
31
+ </html>
32
+ );
33
+ }
src/app/page.tsx ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Navbar from "@/components/global/navbar";
2
+ import { CardBody, CardContainer, CardItem } from "@/components/ui/3d-card";
3
+ import { Button } from "@/components/ui/button";
4
+ import { ContainerScroll } from "@/components/ui/container-scroll-animation";
5
+ import { HeroParallax } from "@/components/ui/hero-parallax";
6
+ import { InfiniteMovingCards } from "@/components/ui/infinite-moving-cards";
7
+ import { LampComponent } from "@/components/ui/lamp";
8
+ import { clients, products } from "@/lib/constants";
9
+ import { CheckIcon } from "lucide-react";
10
+
11
+ export default function Home() {
12
+ return (
13
+ <main className="flex items-center justify-center flex-col">
14
+ <Navbar />
15
+ <section className="h-screen w-full bg-neutral-950 rounded-md !overflow-visible relative flex flex-col items-center antialiased">
16
+ <div className="absolute inset-0 h-full w-full items-center px-5 py-24 [background:radial-gradient(125%_125%_at_50%_10%,#000_35%,#223_100%)]"></div>
17
+ <div className="flex flex-col mt-[-100px] md:mt-[-50px]">
18
+ <ContainerScroll
19
+ titleComponent={
20
+ <div className="flex items-center flex-col">
21
+ <Button
22
+ size={"lg"}
23
+ className="p-8 mb-8 md:mb-0 text-2xl w-full sm:w-fit border-t-2 rounded-full border-[#4D4D4D] bg-[#1F1F1F] hover:bg-white group transition-all flex items-center justify-center gap-4 hover:shadow-xl hover:shadow-neutral-500 duration-500"
24
+ >
25
+ <span className="bg-clip-text text-transparent bg-gradient-to-r from-neutral-500 to-neutral-600 md:text-center font-sans group-hover:bg-gradient-to-r group-hover:from-black goup-hover:to-black">
26
+ Start For Free Today
27
+ </span>
28
+ </Button>
29
+ <h1 className="text-5xl md:text-8xl bg-clip-text text-transparent bg-gradient-to-b from-white to-neutral-600 font-sans font-bold">
30
+ Your Gravity-free Design with Atom
31
+ </h1>
32
+ </div>
33
+ }
34
+ />
35
+ </div>
36
+ </section>
37
+ <InfiniteMovingCards
38
+ className="md:mt-[23rem] mt-[-100px]"
39
+ items={clients}
40
+ direction="right"
41
+ speed="slow"
42
+ />
43
+ <section>
44
+ <HeroParallax products={products} />
45
+ </section>
46
+ <section className="mt-[-500px]">
47
+ <LampComponent />
48
+ <div className="flex flex-wrap items-center justify-center flex-col md:flex-row gap-8 -mt-72">
49
+ <CardContainer className="inter-var ">
50
+ <CardBody className="bg-gray-50 relative group/card dark:hover:shadow-2xl dark:hover:shadow-neutral-500/[0.1] dark:bg-black dark:border-white/[0.2] border-black/[0.1] w-full md:!w-[350px] h-auto rounded-xl p-6 border">
51
+ <CardItem
52
+ translateZ="50"
53
+ className="text-xl font-bold text-neutral-600 dark:text-white "
54
+ >
55
+ Free
56
+ <h2 className="text-6xl ">$0</h2>
57
+ </CardItem>
58
+ <CardItem
59
+ translateZ="60"
60
+ className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
61
+ >
62
+ Get a glimpse of what our software is capable of. Just a heads
63
+ up {"you'll"} never leave us after this!
64
+ <ul className="my-4 flex flex-col gap-2">
65
+ <li className="flex items-center gap-2">
66
+ <CheckIcon className="text-emerald-500" />3 Free automations
67
+ </li>
68
+ <li className="flex items-center gap-2">
69
+ <CheckIcon className="text-emerald-500" />
70
+ 100 tasks per month
71
+ </li>
72
+ <li className="flex items-center gap-2">
73
+ <CheckIcon className="text-emerald-500" />
74
+ Two-step Actions
75
+ </li>
76
+ </ul>
77
+ </CardItem>
78
+ <div className="flex justify-between items-center mt-8">
79
+ <CardItem
80
+ translateZ={20}
81
+ as="button"
82
+ className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
83
+ >
84
+ Try now →
85
+ </CardItem>
86
+ <CardItem
87
+ translateZ={20}
88
+ as="button"
89
+ className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
90
+ >
91
+ Get Started Now
92
+ </CardItem>
93
+ </div>
94
+ </CardBody>
95
+ </CardContainer>
96
+ <CardContainer className="inter-var ">
97
+ <CardBody className="bg-gray-50 relative group/card dark:hover:shadow-2xl dark:hover:shadow-neutral-500/[0.1] dark:bg-black dark:border-[#E2CBFF] border-black/[0.1] w-full md:!w-[350px] h-auto rounded-xl p-6 border">
98
+ <CardItem
99
+ translateZ="50"
100
+ className="text-xl font-bold text-neutral-600 dark:text-white "
101
+ >
102
+ Pro Plan
103
+ <h2 className="text-6xl ">$29</h2>
104
+ </CardItem>
105
+ <CardItem
106
+ translateZ="60"
107
+ className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
108
+ >
109
+ Get a glimpse of what our software is capable of. Just a heads
110
+ up {"you'll"} never leave us after this!
111
+ <ul className="my-4 flex flex-col gap-2">
112
+ <li className="flex items-center gap-2">
113
+ <CheckIcon className="text-emerald-500" />3 Free automations
114
+ </li>
115
+ <li className="flex items-center gap-2">
116
+ <CheckIcon className="text-emerald-500" />
117
+ 100 tasks per month
118
+ </li>
119
+ <li className="flex items-center gap-2">
120
+ <CheckIcon className="text-emerald-500" />
121
+ Two-step Actions
122
+ </li>
123
+ </ul>
124
+ </CardItem>
125
+ <div className="flex justify-between items-center mt-8">
126
+ <CardItem
127
+ translateZ={20}
128
+ as="button"
129
+ className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
130
+ >
131
+ Try now →
132
+ </CardItem>
133
+ <CardItem
134
+ translateZ={20}
135
+ as="button"
136
+ className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
137
+ >
138
+ Get Started Now
139
+ </CardItem>
140
+ </div>
141
+ </CardBody>
142
+ </CardContainer>
143
+ <CardContainer className="inter-var ">
144
+ <CardBody className="bg-gray-50 relative group/card dark:hover:shadow-2xl dark:hover:shadow-neutral-500/[0.1] dark:bg-black dark:border-white/[0.2] border-black/[0.1] w-full md:!w-[350px] h-auto rounded-xl p-6 border">
145
+ <CardItem
146
+ translateZ="50"
147
+ className="text-xl font-bold text-neutral-600 dark:text-white "
148
+ >
149
+ Unlimited
150
+ <h2 className="text-6xl">$99</h2>
151
+ </CardItem>
152
+ <CardItem
153
+ translateZ="60"
154
+ className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
155
+ >
156
+ Get a glimpse of what our software is capable of. Just a heads
157
+ up {"you'll"} never leave us after this!
158
+ <ul className="my-4 flex flex-col gap-2">
159
+ <li className="flex items-center gap-2">
160
+ <CheckIcon className="text-emerald-500" />3 Free automations
161
+ </li>
162
+ <li className="flex items-center gap-2">
163
+ <CheckIcon className="text-emerald-500" />
164
+ 100 tasks per month
165
+ </li>
166
+ <li className="flex items-center gap-2">
167
+ <CheckIcon className="text-emerald-500" />
168
+ Two-step Actions
169
+ </li>
170
+ </ul>
171
+ </CardItem>
172
+ <div className="flex justify-between items-center mt-8">
173
+ <CardItem
174
+ translateZ={20}
175
+ as="button"
176
+ className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
177
+ >
178
+ Try now →
179
+ </CardItem>
180
+ <CardItem
181
+ translateZ={20}
182
+ as="button"
183
+ className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
184
+ >
185
+ Get Started Now
186
+ </CardItem>
187
+ </div>
188
+ </CardBody>
189
+ </CardContainer>
190
+ </div>
191
+ </section>
192
+ </main>
193
+ );
194
+ }
src/components/global/navbar.tsx ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { MenuIcon } from 'lucide-react';
2
+ import Image from 'next/image';
3
+ import Link from 'next/link';
4
+ import React from 'react';
5
+
6
+ type Props = {}
7
+
8
+ const Navbar = async (props: Props) => {
9
+ return (
10
+ <header className='fixed right-0 left-0 top-0 px-4 py-4 bg-black/40 backdrop-blur-lg z-[100] flex items-center border-b-[1px] border-neutral-900 justify-between'>
11
+ <aside className='flex items-center gap-[2px]'>
12
+ <Image src="/logo-transparent.png" alt="logo" width={48} height={48} className='shadow-lg' />
13
+ <p className='text-3xl font-bold'>
14
+ Atom
15
+ </p>
16
+ </aside>
17
+ <nav className=' absolute left-[50%] top-[50%] transform translate-x-[-50%] translate-y-[-50%] hidden md:block'>
18
+ <ul className='flex items-center gap-4 list-none'>
19
+ <li><Link href="#">Products</Link></li>
20
+ <li><Link href="#">Pricing</Link></li>
21
+ <li><Link href="#">Clients</Link></li>
22
+ <li><Link href="#">Resources</Link></li>
23
+ <li><Link href="#">Documentation</Link></li>
24
+ <li><Link href="#">Enterprise</Link></li>
25
+ </ul>
26
+ </nav>
27
+ <aside className='flex items-center gap-4'>
28
+ <Link href="/dashboard"
29
+ className='relative inline-flex h-10 overflow-hidden rounded-full p-[2px] focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50'>
30
+ <span className='absolute inset-[-1000%] animate-[spin_2s_linear_infinite] bg-[conic-gradient(from_90deg_at_50%_50%,#E2CBFF_0%,#393BB2_50%,#E2CBFF_100%)]'></span>
31
+ <span className='inline-flex h-full w-full cursor-pointer items-center justify-center rounded-full bg-slate-950 px-3 py-1 text-sm font-medium text-slate-100 backdrop-blur-3xl'>
32
+ {true ? 'Dashboard' : 'Get Started'}
33
+ </span>
34
+ </Link>
35
+ <MenuIcon className='md:hidden' />
36
+ </aside>
37
+ </header>
38
+ );
39
+ }
40
+
41
+ export default Navbar;
src/components/ui/3d-card.tsx ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { cn } from '@/lib/utils'
4
+ import Image from 'next/image'
5
+ import React, {
6
+ createContext,
7
+ useState,
8
+ useContext,
9
+ useRef,
10
+ useEffect,
11
+ } from 'react'
12
+
13
+ const MouseEnterContext = createContext<
14
+ [boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined
15
+ >(undefined)
16
+
17
+ export const CardContainer = ({
18
+ children,
19
+ className,
20
+ containerClassName,
21
+ }: {
22
+ children?: React.ReactNode
23
+ className?: string
24
+ containerClassName?: string
25
+ }) => {
26
+ const containerRef = useRef<HTMLDivElement>(null)
27
+ const [isMouseEntered, setIsMouseEntered] = useState(false)
28
+
29
+ const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
30
+ if (!containerRef.current) return
31
+ const { left, top, width, height } =
32
+ containerRef.current.getBoundingClientRect()
33
+ const x = (e.clientX - left - width / 2) / 25
34
+ const y = (e.clientY - top - height / 2) / 25
35
+ containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`
36
+ }
37
+
38
+ const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
39
+ setIsMouseEntered(true)
40
+ if (!containerRef.current) return
41
+ }
42
+
43
+ const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
44
+ if (!containerRef.current) return
45
+ setIsMouseEntered(false)
46
+ containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`
47
+ }
48
+ return (
49
+ <MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>
50
+ <div
51
+ className={cn('flex items-center justify-center', containerClassName)}
52
+ style={{
53
+ perspective: '1000px',
54
+ }}
55
+ >
56
+ <div
57
+ ref={containerRef}
58
+ onMouseEnter={handleMouseEnter}
59
+ onMouseMove={handleMouseMove}
60
+ onMouseLeave={handleMouseLeave}
61
+ className={cn(
62
+ 'flex items-center justify-center relative transition-all duration-200 ease-linear',
63
+ className
64
+ )}
65
+ style={{
66
+ transformStyle: 'preserve-3d',
67
+ }}
68
+ >
69
+ {children}
70
+ </div>
71
+ </div>
72
+ </MouseEnterContext.Provider>
73
+ )
74
+ }
75
+
76
+ export const CardBody = ({
77
+ children,
78
+ className,
79
+ }: {
80
+ children: React.ReactNode
81
+ className?: string
82
+ }) => {
83
+ return (
84
+ <div
85
+ className={cn(
86
+ 'h-96 w-96 [transform-style:preserve-3d] [&>*]:[transform-style:preserve-3d]',
87
+ className
88
+ )}
89
+ >
90
+ {children}
91
+ </div>
92
+ )
93
+ }
94
+
95
+ export const CardItem = ({
96
+ as: Tag = 'div',
97
+ children,
98
+ className,
99
+ translateX = 0,
100
+ translateY = 0,
101
+ translateZ = 0,
102
+ rotateX = 0,
103
+ rotateY = 0,
104
+ rotateZ = 0,
105
+ ...rest
106
+ }: {
107
+ as?: React.ElementType
108
+ children: React.ReactNode
109
+ className?: string
110
+ translateX?: number | string
111
+ translateY?: number | string
112
+ translateZ?: number | string
113
+ rotateX?: number | string
114
+ rotateY?: number | string
115
+ rotateZ?: number | string
116
+ }) => {
117
+ const ref = useRef<HTMLDivElement>(null)
118
+ const [isMouseEntered] = useMouseEnter()
119
+
120
+ useEffect(() => {
121
+ handleAnimations()
122
+ }, [isMouseEntered])
123
+
124
+ const handleAnimations = () => {
125
+ if (!ref.current) return
126
+ if (isMouseEntered) {
127
+ ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`
128
+ } else {
129
+ ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`
130
+ }
131
+ }
132
+
133
+ return (
134
+ <Tag
135
+ ref={ref}
136
+ className={cn('w-fit transition duration-200 ease-linear', className)}
137
+ {...rest}
138
+ >
139
+ {children}
140
+ </Tag>
141
+ )
142
+ }
143
+
144
+ // Create a hook to use the context
145
+ export const useMouseEnter = () => {
146
+ const context = useContext(MouseEnterContext)
147
+ if (context === undefined) {
148
+ throw new Error('useMouseEnter must be used within a MouseEnterProvider')
149
+ }
150
+ return context
151
+ }
src/components/ui/button.tsx ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive:
14
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
+ outline:
16
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17
+ secondary:
18
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
+ ghost: "hover:bg-accent hover:text-accent-foreground",
20
+ link: "text-primary underline-offset-4 hover:underline",
21
+ },
22
+ size: {
23
+ default: "h-10 px-4 py-2",
24
+ sm: "h-9 rounded-md px-3",
25
+ lg: "h-11 rounded-md px-8",
26
+ icon: "h-10 w-10",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "default",
31
+ size: "default",
32
+ },
33
+ }
34
+ )
35
+
36
+ export interface ButtonProps
37
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
38
+ VariantProps<typeof buttonVariants> {
39
+ asChild?: boolean
40
+ }
41
+
42
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
43
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
44
+ const Comp = asChild ? Slot : "button"
45
+ return (
46
+ <Comp
47
+ className={cn(buttonVariants({ variant, size, className }))}
48
+ ref={ref}
49
+ {...props}
50
+ />
51
+ )
52
+ }
53
+ )
54
+ Button.displayName = "Button"
55
+
56
+ export { Button, buttonVariants }
src/components/ui/card.tsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-lg border bg-card text-card-foreground shadow-sm",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLParagraphElement,
34
+ React.HTMLAttributes<HTMLHeadingElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <h3
37
+ ref={ref}
38
+ className={cn(
39
+ "text-2xl font-semibold leading-none tracking-tight",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ ))
45
+ CardTitle.displayName = "CardTitle"
46
+
47
+ const CardDescription = React.forwardRef<
48
+ HTMLParagraphElement,
49
+ React.HTMLAttributes<HTMLParagraphElement>
50
+ >(({ className, ...props }, ref) => (
51
+ <p
52
+ ref={ref}
53
+ className={cn("text-sm text-muted-foreground", className)}
54
+ {...props}
55
+ />
56
+ ))
57
+ CardDescription.displayName = "CardDescription"
58
+
59
+ const CardContent = React.forwardRef<
60
+ HTMLDivElement,
61
+ React.HTMLAttributes<HTMLDivElement>
62
+ >(({ className, ...props }, ref) => (
63
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
64
+ ))
65
+ CardContent.displayName = "CardContent"
66
+
67
+ const CardFooter = React.forwardRef<
68
+ HTMLDivElement,
69
+ React.HTMLAttributes<HTMLDivElement>
70
+ >(({ className, ...props }, ref) => (
71
+ <div
72
+ ref={ref}
73
+ className={cn("flex items-center p-6 pt-0", className)}
74
+ {...props}
75
+ />
76
+ ))
77
+ CardFooter.displayName = "CardFooter"
78
+
79
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
src/components/ui/container-scroll-animation.tsx ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import React, { useRef } from 'react'
3
+ import { useScroll, useTransform, motion } from 'framer-motion'
4
+ import Image from 'next/image'
5
+
6
+ export const ContainerScroll = ({
7
+ titleComponent,
8
+ }: {
9
+ titleComponent: string | React.ReactNode
10
+ }) => {
11
+ const containerRef = useRef<any>(null)
12
+ const { scrollYProgress } = useScroll({
13
+ target: containerRef,
14
+ })
15
+ const [isMobile, setIsMobile] = React.useState(false)
16
+
17
+ React.useEffect(() => {
18
+ const checkMobile = () => {
19
+ setIsMobile(window.innerWidth <= 768)
20
+ }
21
+ checkMobile()
22
+ window.addEventListener('resize', checkMobile)
23
+ return () => {
24
+ window.removeEventListener('resize', checkMobile)
25
+ }
26
+ }, [])
27
+
28
+ const scaleDimensions = () => {
29
+ return isMobile ? [0.7, 0.9] : [1.05, 1]
30
+ }
31
+
32
+ const rotate = useTransform(scrollYProgress, [0, 1], [20, 0])
33
+ const scale = useTransform(scrollYProgress, [0, 1], scaleDimensions())
34
+ const translate = useTransform(scrollYProgress, [0, 1], [0, -100])
35
+
36
+ return (
37
+ <div
38
+ className="h-[80rem] flex items-center justify-center relative p-20"
39
+ ref={containerRef}
40
+ >
41
+ <div
42
+ className="py-40 w-full relative"
43
+ style={{
44
+ perspective: '1000px',
45
+ }}
46
+ >
47
+ <Header
48
+ translate={translate}
49
+ titleComponent={titleComponent}
50
+ />
51
+ <Card
52
+ rotate={rotate}
53
+ translate={translate}
54
+ scale={scale}
55
+ />
56
+ </div>
57
+ </div>
58
+ )
59
+ }
60
+
61
+ export const Header = ({ translate, titleComponent }: any) => {
62
+ return (
63
+ <motion.div
64
+ style={{
65
+ translateY: translate,
66
+ }}
67
+ className="div max-w-5xl mx-auto text-center"
68
+ >
69
+ {titleComponent}
70
+ </motion.div>
71
+ )
72
+ }
73
+
74
+ export const Card = ({
75
+ rotate,
76
+ scale,
77
+ translate,
78
+ }: {
79
+ rotate: any
80
+ scale: any
81
+ translate: any
82
+ }) => {
83
+ return (
84
+ <motion.div
85
+ style={{
86
+ rotateX: rotate, // rotate in X-axis
87
+ scale,
88
+ boxShadow:
89
+ '0 0 #0000004d, 0 9px 20px #0000004a, 0 37px 37px #00000042, 0 84px 50px #00000026, 0 149px 60px #0000000a, 0 233px 65px #00000003',
90
+ }}
91
+ className="max-w-5xl -mt-12 mx-auto h-[30rem] md:h-[40rem] w-full p-6 bg-[#222222] rounded-[30px] shadow-2xl"
92
+ >
93
+ <div className="bg-gray-100 h-full w-full rounded-2xl gap-4 overflow-hidden p-4 transition-all ">
94
+ <Image
95
+ src="/images/temp-banner.png"
96
+ fill
97
+ alt="bannerImage"
98
+ className="object-cover border-8 rounded-2xl"
99
+ />
100
+ </div>
101
+ </motion.div>
102
+ )
103
+ }
src/components/ui/hero-parallax.tsx ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+ import React from 'react'
3
+ import {
4
+ motion,
5
+ useScroll,
6
+ useTransform,
7
+ useSpring,
8
+ MotionValue,
9
+ } from 'framer-motion'
10
+ import Image from 'next/image'
11
+ import Link from 'next/link'
12
+
13
+ export const HeroParallax = ({
14
+ products,
15
+ }: {
16
+ products: {
17
+ title: string
18
+ link: string
19
+ thumbnail: string
20
+ }[]
21
+ }) => {
22
+ const firstRow = products.slice(0, 5)
23
+ const secondRow = products.slice(5, 10)
24
+ const thirdRow = products.slice(10, 15)
25
+ const ref = React.useRef(null)
26
+ const { scrollYProgress } = useScroll({
27
+ target: ref,
28
+ offset: ['start start', 'end start'],
29
+ })
30
+
31
+ const springConfig = { stiffness: 300, damping: 30, bounce: 100 }
32
+
33
+ const translateX = useSpring(
34
+ useTransform(scrollYProgress, [0, 1], [0, 1000]),
35
+ springConfig
36
+ )
37
+ const translateXReverse = useSpring(
38
+ useTransform(scrollYProgress, [0, 1], [0, -1000]),
39
+ springConfig
40
+ )
41
+ const rotateX = useSpring(
42
+ useTransform(scrollYProgress, [0, 0.2], [15, 0]),
43
+ springConfig
44
+ )
45
+ const opacity = useSpring(
46
+ useTransform(scrollYProgress, [0, 0.2], [0.2, 1]),
47
+ springConfig
48
+ )
49
+ const rotateZ = useSpring(
50
+ useTransform(scrollYProgress, [0, 0.2], [20, 0]),
51
+ springConfig
52
+ )
53
+ const translateY = useSpring(
54
+ useTransform(scrollYProgress, [0, 0.2], [-700, 500]),
55
+ springConfig
56
+ )
57
+ return (
58
+ <div
59
+ ref={ref}
60
+ className="h-[300vh] py-40 overflow-hidden antialiased relative flex flex-col self-auto [perspective:1000px] [transform-style:preserve-3d]"
61
+ >
62
+ <Header />
63
+ <motion.div
64
+ style={{
65
+ rotateX,
66
+ rotateZ,
67
+ translateY,
68
+ opacity,
69
+ }}
70
+ className=""
71
+ >
72
+ <motion.div className="flex flex-row-reverse space-x-reverse space-x-20 mb-20">
73
+ {firstRow.map((product) => (
74
+ <ProductCard
75
+ product={product}
76
+ translate={translateX}
77
+ key={product.title}
78
+ />
79
+ ))}
80
+ </motion.div>
81
+ <motion.div className="flex flex-row mb-20 space-x-20 ">
82
+ {secondRow.map((product) => (
83
+ <ProductCard
84
+ product={product}
85
+ translate={translateXReverse}
86
+ key={product.title}
87
+ />
88
+ ))}
89
+ </motion.div>
90
+ <motion.div className="flex flex-row-reverse space-x-reverse space-x-20">
91
+ {thirdRow.map((product) => (
92
+ <ProductCard
93
+ product={product}
94
+ translate={translateX}
95
+ key={product.title}
96
+ />
97
+ ))}
98
+ </motion.div>
99
+ </motion.div>
100
+ </div>
101
+ )
102
+ }
103
+
104
+ export const Header = () => {
105
+ return (
106
+ <div className="max-w-7xl relative mx-auto py-20 md:py-40 px-4 w-full left-0 top-0">
107
+ <h1 className="text-2xl md:text-7xl font-bold dark:text-white">
108
+ The Ultimate <br /> development studio
109
+ </h1>
110
+ <p className="max-w-2xl text-base md:text-xl mt-8 dark:text-neutral-200">
111
+ We build beautiful products with the latest technologies and frameworks.
112
+ We are a team of passionate developers and designers that love to build
113
+ amazing products.
114
+ </p>
115
+ </div>
116
+ )
117
+ }
118
+
119
+ export const ProductCard = ({
120
+ product,
121
+ translate,
122
+ }: {
123
+ product: {
124
+ title: string
125
+ link: string
126
+ thumbnail: string
127
+ }
128
+ translate: MotionValue<number>
129
+ }) => {
130
+ return (
131
+ <motion.div
132
+ style={{
133
+ x: translate,
134
+ }}
135
+ whileHover={{
136
+ y: -20,
137
+ }}
138
+ key={product.title}
139
+ className="group/product h-96 w-[30rem] relative flex-shrink-0"
140
+ >
141
+ <Link
142
+ href={product.link}
143
+ className="block group-hover/product:shadow-2xl "
144
+ >
145
+ <Image
146
+ src={product.thumbnail}
147
+ height="600"
148
+ width="600"
149
+ className="object-cover object-left-top absolute h-full w-full inset-0"
150
+ alt={product.title}
151
+ />
152
+ </Link>
153
+ <div className="absolute inset-0 h-full w-full opacity-0 group-hover/product:opacity-80 bg-black pointer-events-none"></div>
154
+ <h2 className="absolute bottom-4 left-4 opacity-0 group-hover/product:opacity-100 text-white">
155
+ {product.title}
156
+ </h2>
157
+ </motion.div>
158
+ )
159
+ }
src/components/ui/infinite-moving-cards.tsx ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import { cn } from '@/lib/utils'
4
+ import Image from 'next/image'
5
+ import React, { useEffect, useState } from 'react'
6
+
7
+ export const InfiniteMovingCards = ({
8
+ items,
9
+ direction = 'left',
10
+ speed = 'fast',
11
+ pauseOnHover = true,
12
+ className,
13
+ }: {
14
+ items: {
15
+ href: string
16
+ }[]
17
+ direction?: 'left' | 'right'
18
+ speed?: 'fast' | 'normal' | 'slow'
19
+ pauseOnHover?: boolean
20
+ className?: string
21
+ }) => {
22
+ const containerRef = React.useRef<HTMLDivElement>(null)
23
+ const scrollerRef = React.useRef<HTMLUListElement>(null)
24
+
25
+ useEffect(() => {
26
+ addAnimation()
27
+ }, [])
28
+
29
+ const [start, setStart] = useState(false)
30
+ function addAnimation() {
31
+ if (containerRef.current && scrollerRef.current) {
32
+ const scrollerContent = Array.from(scrollerRef.current.children)
33
+
34
+ scrollerContent.forEach((item) => {
35
+ const duplicatedItem = item.cloneNode(true)
36
+ if (scrollerRef.current) {
37
+ scrollerRef.current.appendChild(duplicatedItem)
38
+ }
39
+ })
40
+
41
+ getDirection()
42
+ getSpeed()
43
+ setStart(true)
44
+ }
45
+ }
46
+ const getDirection = () => {
47
+ if (containerRef.current) {
48
+ if (direction === 'left') {
49
+ containerRef.current.style.setProperty(
50
+ '--animation-direction',
51
+ 'forwards'
52
+ )
53
+ } else {
54
+ containerRef.current.style.setProperty(
55
+ '--animation-direction',
56
+ 'reverse'
57
+ )
58
+ }
59
+ }
60
+ }
61
+ const getSpeed = () => {
62
+ if (containerRef.current) {
63
+ if (speed === 'fast') {
64
+ containerRef.current.style.setProperty('--animation-duration', '20s')
65
+ } else if (speed === 'normal') {
66
+ containerRef.current.style.setProperty('--animation-duration', '40s')
67
+ } else {
68
+ containerRef.current.style.setProperty('--animation-duration', '80s')
69
+ }
70
+ }
71
+ }
72
+ console.log(items)
73
+ return (
74
+ <div
75
+ ref={containerRef}
76
+ className={cn(
77
+ 'scroller relative z-20 max-w-7xl overflow-hidden [mask-image:linear-gradient(to_right,transparent,white_20%,white_80%,transparent)]',
78
+ className
79
+ )}
80
+ >
81
+ <ul
82
+ ref={scrollerRef}
83
+ className={cn(
84
+ ' flex min-w-full shrink-0 gap-10 py-4 w-max flex-nowrap',
85
+ start && 'animate-scroll ',
86
+ pauseOnHover && 'hover:[animation-play-state:paused]'
87
+ )}
88
+ >
89
+ {items.map((item, idx) => (
90
+ <Image
91
+ width={170}
92
+ height={1}
93
+ src={item.href}
94
+ alt={item.href}
95
+ className="relative grayscale rounded-2xl object-contain opacity-50"
96
+ key={item.href}
97
+ />
98
+ ))}
99
+ </ul>
100
+ </div>
101
+ )
102
+ }