Spaces:
Running
Running
Florin Bobiș
commited on
Commit
·
da36ada
1
Parent(s):
e1becaa
app added
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .eslintrc.json +3 -0
- .gitignore +38 -0
- Dockerfile +23 -0
- components.json +17 -0
- next.config.mjs +4 -0
- package-lock.json +0 -0
- package.json +37 -0
- postcss.config.mjs +8 -0
- public/images/1.png +0 -0
- public/images/10.png +0 -0
- public/images/2.png +0 -0
- public/images/3.png +0 -0
- public/images/4.png +0 -0
- public/images/5.png +0 -0
- public/images/6.png +0 -0
- public/images/7.png +0 -0
- public/images/8.png +0 -0
- public/images/9.png +0 -0
- public/images/client1.webp +0 -0
- public/images/client10.webp +0 -0
- public/images/client11.webp +0 -0
- public/images/client12.webp +0 -0
- public/images/client13.webp +0 -0
- public/images/client2.webp +0 -0
- public/images/client3.webp +0 -0
- public/images/client4.webp +0 -0
- public/images/client5.webp +0 -0
- public/images/client6.webp +0 -0
- public/images/client7.webp +0 -0
- public/images/client8.webp +0 -0
- public/images/client9.webp +0 -0
- public/images/p1.png +0 -0
- public/images/p2.png +0 -0
- public/images/p3.png +0 -0
- public/images/p4.png +0 -0
- public/images/temp-banner.png +0 -0
- public/logo-dark.png +0 -0
- public/logo-transparent.png +0 -0
- public/logo.png +0 -0
- src/app/favicon.ico +0 -0
- src/app/globals.css +76 -0
- src/app/layout.tsx +33 -0
- src/app/page.tsx +194 -0
- src/components/global/navbar.tsx +41 -0
- src/components/ui/3d-card.tsx +151 -0
- src/components/ui/button.tsx +56 -0
- src/components/ui/card.tsx +79 -0
- src/components/ui/container-scroll-animation.tsx +103 -0
- src/components/ui/hero-parallax.tsx +159 -0
- 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 |
+
}
|