Spaces:
Running
Running
Florin Bobiș
commited on
Commit
·
3456137
1
Parent(s):
29d1cf9
better
Browse files- src/app/page.tsx +4 -25
- src/components/about-section.tsx +35 -0
- src/components/footer.tsx +2 -2
- src/components/header.tsx +1 -1
- src/components/navbar.tsx +4 -5
- src/components/pricing-section.tsx +156 -0
- src/components/ui/3d-card.tsx +150 -0
- src/components/ui/card.tsx +86 -0
- src/components/ui/lamp.tsx +117 -0
src/app/page.tsx
CHANGED
@@ -1,10 +1,10 @@
|
|
|
|
1 |
import Footer from "@/components/footer";
|
2 |
import Header from "@/components/header";
|
|
|
3 |
import { Compare } from "@/components/ui/compare";
|
4 |
-
import { ContainerScroll } from "@/components/ui/container-scroll-animation";
|
5 |
import { Cover } from "@/components/ui/cover";
|
6 |
import WelcomeSection from "@/components/welcome-section";
|
7 |
-
import Image from "next/image";
|
8 |
|
9 |
export default function Home() {
|
10 |
return (
|
@@ -12,29 +12,7 @@ export default function Home() {
|
|
12 |
<Header />
|
13 |
<main className="flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col">
|
14 |
<WelcomeSection />
|
15 |
-
<
|
16 |
-
<ContainerScroll
|
17 |
-
titleComponent={
|
18 |
-
<>
|
19 |
-
<h1 className="p-2 text-4xl font-semibold bg-clip-text text-transparent text-center bg-gradient-to-b from-neutral-900 to-neutral-700 dark:from-neutral-600 dark:to-white">
|
20 |
-
Discover the power of <br />
|
21 |
-
<span className="text-4xl md:text-[6rem] font-bold mt-1 leading-none">
|
22 |
-
Microgravity
|
23 |
-
</span>
|
24 |
-
</h1>
|
25 |
-
</>
|
26 |
-
}
|
27 |
-
>
|
28 |
-
<Image
|
29 |
-
src={`/screenshot.jpg`}
|
30 |
-
alt="hero"
|
31 |
-
height={720}
|
32 |
-
width={1400}
|
33 |
-
className="mx-auto rounded-2xl object-cover h-full object-left-top"
|
34 |
-
draggable={false}
|
35 |
-
/>
|
36 |
-
</ContainerScroll>
|
37 |
-
</section>
|
38 |
<section className="flex flex-col overflow-hidden bg-background dark:bg-black h-[calc(100vh_-_theme(spacing.16))] items-center justify-center">
|
39 |
<h1 className="text-4xl md:text-4xl lg:text-6xl font-semibold max-w-7xl mx-auto text-center mt-6 relative z-20 py-6 bg-clip-text text-transparent bg-gradient-to-b from-neutral-800 via-neutral-700 to-neutral-700 dark:from-neutral-800 dark:via-white dark:to-white">
|
40 |
Get amazing insights <br /> at <Cover>warp speed</Cover>
|
@@ -50,6 +28,7 @@ export default function Home() {
|
|
50 |
/>
|
51 |
</div>
|
52 |
</section>
|
|
|
53 |
</main>
|
54 |
<Footer />
|
55 |
</div>
|
|
|
1 |
+
import AboutSection from "@/components/about-section";
|
2 |
import Footer from "@/components/footer";
|
3 |
import Header from "@/components/header";
|
4 |
+
import PricingSection from "@/components/pricing-section";
|
5 |
import { Compare } from "@/components/ui/compare";
|
|
|
6 |
import { Cover } from "@/components/ui/cover";
|
7 |
import WelcomeSection from "@/components/welcome-section";
|
|
|
8 |
|
9 |
export default function Home() {
|
10 |
return (
|
|
|
12 |
<Header />
|
13 |
<main className="flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col">
|
14 |
<WelcomeSection />
|
15 |
+
<AboutSection />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
<section className="flex flex-col overflow-hidden bg-background dark:bg-black h-[calc(100vh_-_theme(spacing.16))] items-center justify-center">
|
17 |
<h1 className="text-4xl md:text-4xl lg:text-6xl font-semibold max-w-7xl mx-auto text-center mt-6 relative z-20 py-6 bg-clip-text text-transparent bg-gradient-to-b from-neutral-800 via-neutral-700 to-neutral-700 dark:from-neutral-800 dark:via-white dark:to-white">
|
18 |
Get amazing insights <br /> at <Cover>warp speed</Cover>
|
|
|
28 |
/>
|
29 |
</div>
|
30 |
</section>
|
31 |
+
<PricingSection />
|
32 |
</main>
|
33 |
<Footer />
|
34 |
</div>
|
src/components/about-section.tsx
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Image from "next/image";
|
2 |
+
import { ContainerScroll } from "./ui/container-scroll-animation";
|
3 |
+
|
4 |
+
const AboutSection = () => {
|
5 |
+
return (
|
6 |
+
<section
|
7 |
+
id="about"
|
8 |
+
className="flex flex-col overflow-hidden bg-background dark:bg-black"
|
9 |
+
>
|
10 |
+
<ContainerScroll
|
11 |
+
titleComponent={
|
12 |
+
<>
|
13 |
+
<h1 className="p-2 text-4xl font-semibold bg-clip-text text-transparent text-center bg-gradient-to-b from-neutral-900 to-neutral-700 dark:from-neutral-600 dark:to-white">
|
14 |
+
Discover the power of <br />
|
15 |
+
<span className="text-4xl md:text-[6rem] font-bold mt-1 leading-none">
|
16 |
+
Microgravity
|
17 |
+
</span>
|
18 |
+
</h1>
|
19 |
+
</>
|
20 |
+
}
|
21 |
+
>
|
22 |
+
<Image
|
23 |
+
src={`/screenshot.jpg`}
|
24 |
+
alt="hero"
|
25 |
+
height={720}
|
26 |
+
width={1400}
|
27 |
+
className="mx-auto rounded-2xl object-cover h-full object-left-top"
|
28 |
+
draggable={false}
|
29 |
+
/>
|
30 |
+
</ContainerScroll>
|
31 |
+
</section>
|
32 |
+
);
|
33 |
+
};
|
34 |
+
|
35 |
+
export default AboutSection;
|
src/components/footer.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
const Footer = () => {
|
2 |
return (
|
3 |
-
<footer className="sticky bottom-0 flex h-
|
4 |
-
<span className="text-
|
5 |
</footer>
|
6 |
);
|
7 |
};
|
|
|
1 |
const Footer = () => {
|
2 |
return (
|
3 |
+
<footer className="sticky bottom-0 flex h-12 items-center justify-center border-t gap-4 bg-background/50 backdrop-blur px-4 mt-10 md:px-6 z-30">
|
4 |
+
<span className="text-md text-muted-foreground">© ATOM 2024</span>
|
5 |
</footer>
|
6 |
);
|
7 |
};
|
src/components/header.tsx
CHANGED
@@ -6,7 +6,7 @@ import { Button } from "./ui/button";
|
|
6 |
|
7 |
const Header = () => {
|
8 |
return (
|
9 |
-
<header className="sticky top-0 flex h-16 items-center border-b shadow-md gap-4 bg-background/
|
10 |
<Navbar />
|
11 |
<Sidebar />
|
12 |
<div className="flex w-full md:w-auto justify-end gap-4 md:ml-auto md:gap-2 lg:gap-4">
|
|
|
6 |
|
7 |
const Header = () => {
|
8 |
return (
|
9 |
+
<header className="sticky top-0 flex h-16 items-center border-b shadow-md gap-4 bg-background/50 backdrop-blur px-4 md:px-6 z-30">
|
10 |
<Navbar />
|
11 |
<Sidebar />
|
12 |
<div className="flex w-full md:w-auto justify-end gap-4 md:ml-auto md:gap-2 lg:gap-4">
|
src/components/navbar.tsx
CHANGED
@@ -7,11 +7,10 @@ import Link from "next/link";
|
|
7 |
const Navbar = () => {
|
8 |
const { theme } = useTheme();
|
9 |
const links = [
|
10 |
-
{ name: "
|
11 |
-
{ name: "
|
12 |
-
{ name: "
|
13 |
-
{ name: "
|
14 |
-
{ name: "Contact", href: "#" },
|
15 |
];
|
16 |
return (
|
17 |
<nav className="hidden flex-col gap-6 w-full text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6">
|
|
|
7 |
const Navbar = () => {
|
8 |
const { theme } = useTheme();
|
9 |
const links = [
|
10 |
+
{ name: "About", href: "#about" },
|
11 |
+
{ name: "Clients", href: "#clients" },
|
12 |
+
{ name: "Pricing", href: "#pricing" },
|
13 |
+
{ name: "Contact", href: "#contact" },
|
|
|
14 |
];
|
15 |
return (
|
16 |
<nav className="hidden flex-col gap-6 w-full text-lg font-medium md:flex md:flex-row md:items-center md:gap-5 md:text-sm lg:gap-6">
|
src/components/pricing-section.tsx
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { CheckIcon } from "lucide-react";
|
2 |
+
import { LampComponent } from "./ui/lamp";
|
3 |
+
import { CardContainer, CardBody, CardItem } from "./ui/3d-card";
|
4 |
+
|
5 |
+
const PricingSection = () => {
|
6 |
+
return (
|
7 |
+
<section id="pricing" className="">
|
8 |
+
<LampComponent />
|
9 |
+
<div className="flex flex-wrap items-center justify-center flex-col md:flex-row gap-8 -mt-72">
|
10 |
+
<CardContainer className="inter-var ">
|
11 |
+
<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">
|
12 |
+
<CardItem
|
13 |
+
translateZ="50"
|
14 |
+
className="text-xl font-bold text-neutral-600 dark:text-white "
|
15 |
+
>
|
16 |
+
Free
|
17 |
+
<h2 className="text-6xl ">$0</h2>
|
18 |
+
</CardItem>
|
19 |
+
<CardItem
|
20 |
+
translateZ="60"
|
21 |
+
className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
|
22 |
+
>
|
23 |
+
Get a glimpse of what our software is capable of. Just a heads up{" "}
|
24 |
+
{"you'll"} never leave us after this!
|
25 |
+
<ul className="my-4 flex flex-col gap-2">
|
26 |
+
<li className="flex items-center gap-2">
|
27 |
+
<CheckIcon className="text-emerald-500" />3 Free automations
|
28 |
+
</li>
|
29 |
+
<li className="flex items-center gap-2">
|
30 |
+
<CheckIcon className="text-emerald-500" />
|
31 |
+
100 tasks per month
|
32 |
+
</li>
|
33 |
+
<li className="flex items-center gap-2">
|
34 |
+
<CheckIcon className="text-emerald-500" />
|
35 |
+
Two-step Actions
|
36 |
+
</li>
|
37 |
+
</ul>
|
38 |
+
</CardItem>
|
39 |
+
<div className="flex justify-between items-center mt-8">
|
40 |
+
<CardItem
|
41 |
+
translateZ={20}
|
42 |
+
as="button"
|
43 |
+
className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
|
44 |
+
>
|
45 |
+
Try now →
|
46 |
+
</CardItem>
|
47 |
+
<CardItem
|
48 |
+
translateZ={20}
|
49 |
+
as="button"
|
50 |
+
className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
|
51 |
+
>
|
52 |
+
Get Started Now
|
53 |
+
</CardItem>
|
54 |
+
</div>
|
55 |
+
</CardBody>
|
56 |
+
</CardContainer>
|
57 |
+
<CardContainer className="inter-var ">
|
58 |
+
<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">
|
59 |
+
<CardItem
|
60 |
+
translateZ="50"
|
61 |
+
className="text-xl font-bold text-neutral-600 dark:text-white "
|
62 |
+
>
|
63 |
+
Pro Plan
|
64 |
+
<h2 className="text-6xl ">$29</h2>
|
65 |
+
</CardItem>
|
66 |
+
<CardItem
|
67 |
+
translateZ="60"
|
68 |
+
className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
|
69 |
+
>
|
70 |
+
Get a glimpse of what our software is capable of. Just a heads up{" "}
|
71 |
+
{"you'll"} never leave us after this!
|
72 |
+
<ul className="my-4 flex flex-col gap-2">
|
73 |
+
<li className="flex items-center gap-2">
|
74 |
+
<CheckIcon className="text-emerald-500" />3 Free automations
|
75 |
+
</li>
|
76 |
+
<li className="flex items-center gap-2">
|
77 |
+
<CheckIcon className="text-emerald-500" />
|
78 |
+
100 tasks per month
|
79 |
+
</li>
|
80 |
+
<li className="flex items-center gap-2">
|
81 |
+
<CheckIcon className="text-emerald-500" />
|
82 |
+
Two-step Actions
|
83 |
+
</li>
|
84 |
+
</ul>
|
85 |
+
</CardItem>
|
86 |
+
<div className="flex justify-between items-center mt-8">
|
87 |
+
<CardItem
|
88 |
+
translateZ={20}
|
89 |
+
as="button"
|
90 |
+
className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
|
91 |
+
>
|
92 |
+
Try now →
|
93 |
+
</CardItem>
|
94 |
+
<CardItem
|
95 |
+
translateZ={20}
|
96 |
+
as="button"
|
97 |
+
className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
|
98 |
+
>
|
99 |
+
Get Started Now
|
100 |
+
</CardItem>
|
101 |
+
</div>
|
102 |
+
</CardBody>
|
103 |
+
</CardContainer>
|
104 |
+
<CardContainer className="inter-var ">
|
105 |
+
<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">
|
106 |
+
<CardItem
|
107 |
+
translateZ="50"
|
108 |
+
className="text-xl font-bold text-neutral-600 dark:text-white "
|
109 |
+
>
|
110 |
+
Unlimited
|
111 |
+
<h2 className="text-6xl">$99</h2>
|
112 |
+
</CardItem>
|
113 |
+
<CardItem
|
114 |
+
translateZ="60"
|
115 |
+
className="text-neutral-500 text-sm max-w-sm mt-2 dark:text-neutral-300"
|
116 |
+
>
|
117 |
+
Get a glimpse of what our software is capable of. Just a heads up{" "}
|
118 |
+
{"you'll"} never leave us after this!
|
119 |
+
<ul className="my-4 flex flex-col gap-2">
|
120 |
+
<li className="flex items-center gap-2">
|
121 |
+
<CheckIcon className="text-emerald-500" />3 Free automations
|
122 |
+
</li>
|
123 |
+
<li className="flex items-center gap-2">
|
124 |
+
<CheckIcon className="text-emerald-500" />
|
125 |
+
100 tasks per month
|
126 |
+
</li>
|
127 |
+
<li className="flex items-center gap-2">
|
128 |
+
<CheckIcon className="text-emerald-500" />
|
129 |
+
Two-step Actions
|
130 |
+
</li>
|
131 |
+
</ul>
|
132 |
+
</CardItem>
|
133 |
+
<div className="flex justify-between items-center mt-8">
|
134 |
+
<CardItem
|
135 |
+
translateZ={20}
|
136 |
+
as="button"
|
137 |
+
className="px-4 py-2 rounded-xl text-xs font-normal dark:text-white"
|
138 |
+
>
|
139 |
+
Try now →
|
140 |
+
</CardItem>
|
141 |
+
<CardItem
|
142 |
+
translateZ={20}
|
143 |
+
as="button"
|
144 |
+
className="px-4 py-2 rounded-xl bg-black dark:bg-white dark:text-black text-white text-xs font-bold"
|
145 |
+
>
|
146 |
+
Get Started Now
|
147 |
+
</CardItem>
|
148 |
+
</div>
|
149 |
+
</CardBody>
|
150 |
+
</CardContainer>
|
151 |
+
</div>
|
152 |
+
</section>
|
153 |
+
);
|
154 |
+
};
|
155 |
+
|
156 |
+
export default PricingSection;
|
src/components/ui/3d-card.tsx
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
|
3 |
+
import { cn } from "@/lib/utils";
|
4 |
+
import React, {
|
5 |
+
createContext,
|
6 |
+
useState,
|
7 |
+
useContext,
|
8 |
+
useRef,
|
9 |
+
useEffect,
|
10 |
+
} from "react";
|
11 |
+
|
12 |
+
const MouseEnterContext = createContext<
|
13 |
+
[boolean, React.Dispatch<React.SetStateAction<boolean>>] | undefined
|
14 |
+
>(undefined);
|
15 |
+
|
16 |
+
export const CardContainer = ({
|
17 |
+
children,
|
18 |
+
className,
|
19 |
+
containerClassName,
|
20 |
+
}: {
|
21 |
+
children?: React.ReactNode;
|
22 |
+
className?: string;
|
23 |
+
containerClassName?: string;
|
24 |
+
}) => {
|
25 |
+
const containerRef = useRef<HTMLDivElement>(null);
|
26 |
+
const [isMouseEntered, setIsMouseEntered] = useState(false);
|
27 |
+
|
28 |
+
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
29 |
+
if (!containerRef.current) return;
|
30 |
+
const { left, top, width, height } =
|
31 |
+
containerRef.current.getBoundingClientRect();
|
32 |
+
const x = (e.clientX - left - width / 2) / 25;
|
33 |
+
const y = (e.clientY - top - height / 2) / 25;
|
34 |
+
containerRef.current.style.transform = `rotateY(${x}deg) rotateX(${y}deg)`;
|
35 |
+
};
|
36 |
+
|
37 |
+
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>) => {
|
38 |
+
setIsMouseEntered(true);
|
39 |
+
if (!containerRef.current) return;
|
40 |
+
};
|
41 |
+
|
42 |
+
const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
|
43 |
+
if (!containerRef.current) return;
|
44 |
+
setIsMouseEntered(false);
|
45 |
+
containerRef.current.style.transform = `rotateY(0deg) rotateX(0deg)`;
|
46 |
+
};
|
47 |
+
return (
|
48 |
+
<MouseEnterContext.Provider value={[isMouseEntered, setIsMouseEntered]}>
|
49 |
+
<div
|
50 |
+
className={cn("flex items-center justify-center", containerClassName)}
|
51 |
+
style={{
|
52 |
+
perspective: "1000px",
|
53 |
+
}}
|
54 |
+
>
|
55 |
+
<div
|
56 |
+
ref={containerRef}
|
57 |
+
onMouseEnter={handleMouseEnter}
|
58 |
+
onMouseMove={handleMouseMove}
|
59 |
+
onMouseLeave={handleMouseLeave}
|
60 |
+
className={cn(
|
61 |
+
"flex items-center justify-center relative transition-all duration-200 ease-linear",
|
62 |
+
className
|
63 |
+
)}
|
64 |
+
style={{
|
65 |
+
transformStyle: "preserve-3d",
|
66 |
+
}}
|
67 |
+
>
|
68 |
+
{children}
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</MouseEnterContext.Provider>
|
72 |
+
);
|
73 |
+
};
|
74 |
+
|
75 |
+
export const CardBody = ({
|
76 |
+
children,
|
77 |
+
className,
|
78 |
+
}: {
|
79 |
+
children: React.ReactNode;
|
80 |
+
className?: string;
|
81 |
+
}) => {
|
82 |
+
return (
|
83 |
+
<div
|
84 |
+
className={cn(
|
85 |
+
"h-96 w-96 [transform-style:preserve-3d] [&>*]:[transform-style:preserve-3d]",
|
86 |
+
className
|
87 |
+
)}
|
88 |
+
>
|
89 |
+
{children}
|
90 |
+
</div>
|
91 |
+
);
|
92 |
+
};
|
93 |
+
|
94 |
+
export const CardItem = ({
|
95 |
+
as: Tag = "div",
|
96 |
+
children,
|
97 |
+
className,
|
98 |
+
translateX = 0,
|
99 |
+
translateY = 0,
|
100 |
+
translateZ = 0,
|
101 |
+
rotateX = 0,
|
102 |
+
rotateY = 0,
|
103 |
+
rotateZ = 0,
|
104 |
+
...rest
|
105 |
+
}: {
|
106 |
+
as?: React.ElementType;
|
107 |
+
children: React.ReactNode;
|
108 |
+
className?: string;
|
109 |
+
translateX?: number | string;
|
110 |
+
translateY?: number | string;
|
111 |
+
translateZ?: number | string;
|
112 |
+
rotateX?: number | string;
|
113 |
+
rotateY?: number | string;
|
114 |
+
rotateZ?: number | string;
|
115 |
+
}) => {
|
116 |
+
const ref = useRef<HTMLDivElement>(null);
|
117 |
+
const [isMouseEntered] = useMouseEnter();
|
118 |
+
|
119 |
+
useEffect(() => {
|
120 |
+
handleAnimations();
|
121 |
+
}, [isMouseEntered]);
|
122 |
+
|
123 |
+
const handleAnimations = () => {
|
124 |
+
if (!ref.current) return;
|
125 |
+
if (isMouseEntered) {
|
126 |
+
ref.current.style.transform = `translateX(${translateX}px) translateY(${translateY}px) translateZ(${translateZ}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotateZ(${rotateZ}deg)`;
|
127 |
+
} else {
|
128 |
+
ref.current.style.transform = `translateX(0px) translateY(0px) translateZ(0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg)`;
|
129 |
+
}
|
130 |
+
};
|
131 |
+
|
132 |
+
return (
|
133 |
+
<Tag
|
134 |
+
ref={ref}
|
135 |
+
className={cn("w-fit transition duration-200 ease-linear", className)}
|
136 |
+
{...rest}
|
137 |
+
>
|
138 |
+
{children}
|
139 |
+
</Tag>
|
140 |
+
);
|
141 |
+
};
|
142 |
+
|
143 |
+
// Create a hook to use the context
|
144 |
+
export const useMouseEnter = () => {
|
145 |
+
const context = useContext(MouseEnterContext);
|
146 |
+
if (context === undefined) {
|
147 |
+
throw new Error("useMouseEnter must be used within a MouseEnterProvider");
|
148 |
+
}
|
149 |
+
return context;
|
150 |
+
};
|
src/components/ui/card.tsx
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 {
|
80 |
+
Card,
|
81 |
+
CardHeader,
|
82 |
+
CardFooter,
|
83 |
+
CardTitle,
|
84 |
+
CardDescription,
|
85 |
+
CardContent,
|
86 |
+
};
|
src/components/ui/lamp.tsx
ADDED
@@ -0,0 +1,117 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
import React from "react";
|
3 |
+
import { motion } from "framer-motion";
|
4 |
+
import { cn } from "@/lib/utils";
|
5 |
+
import { SparklesCore } from "./sparkles";
|
6 |
+
|
7 |
+
export function LampComponent() {
|
8 |
+
return (
|
9 |
+
<LampContainer>
|
10 |
+
<motion.h1
|
11 |
+
initial={{ opacity: 0.5, y: 100 }}
|
12 |
+
whileInView={{ opacity: 1, y: 0 }}
|
13 |
+
transition={{
|
14 |
+
delay: 0.3,
|
15 |
+
duration: 0.8,
|
16 |
+
ease: "easeInOut",
|
17 |
+
}}
|
18 |
+
className="mt-20 bg-gradient-to-br from-neutral-300 to-neutral-500 py-4 bg-clip-text text-center text-4xl font-medium tracking-tight text-transparent md:text-7xl"
|
19 |
+
>
|
20 |
+
Plans That
|
21 |
+
<br /> Fit You Best
|
22 |
+
</motion.h1>
|
23 |
+
</LampContainer>
|
24 |
+
);
|
25 |
+
}
|
26 |
+
|
27 |
+
export const LampContainer = ({
|
28 |
+
children,
|
29 |
+
className,
|
30 |
+
}: {
|
31 |
+
children: React.ReactNode;
|
32 |
+
className?: string;
|
33 |
+
}) => {
|
34 |
+
return (
|
35 |
+
<div
|
36 |
+
className={cn(
|
37 |
+
"relative flex min-h-[800px] flex-col items-center justify-center overflow-hidden bg-neutral-950 w-full rounded-md z-0",
|
38 |
+
className
|
39 |
+
)}
|
40 |
+
>
|
41 |
+
<div className="relative flex w-full flex-1 scale-y-125 items-center justify-center isolate z-0 ">
|
42 |
+
<motion.div
|
43 |
+
initial={{ opacity: 0.5, width: "15rem" }}
|
44 |
+
whileInView={{ opacity: 1, width: "30rem" }}
|
45 |
+
transition={{
|
46 |
+
delay: 0.3,
|
47 |
+
duration: 0.8,
|
48 |
+
ease: "easeInOut",
|
49 |
+
}}
|
50 |
+
style={{
|
51 |
+
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
|
52 |
+
}}
|
53 |
+
className="absolute inset-auto right-1/2 h-56 overflow-visible w-[30rem] bg-gradient-conic from-neutral-500 via-transparent to-transparent text-white [--conic-position:from_70deg_at_center_top]"
|
54 |
+
>
|
55 |
+
<div className="absolute w-[100%] left-0 bg-neutral-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
|
56 |
+
<div className="absolute w-40 h-[100%] left-0 bg-neutral-950 bottom-0 z-20 [mask-image:linear-gradient(to_right,white,transparent)]" />
|
57 |
+
</motion.div>
|
58 |
+
<motion.div
|
59 |
+
initial={{ opacity: 0.5, width: "15rem" }}
|
60 |
+
whileInView={{ opacity: 1, width: "30rem" }}
|
61 |
+
transition={{
|
62 |
+
delay: 0.3,
|
63 |
+
duration: 0.8,
|
64 |
+
ease: "easeInOut",
|
65 |
+
}}
|
66 |
+
style={{
|
67 |
+
backgroundImage: `conic-gradient(var(--conic-position), var(--tw-gradient-stops))`,
|
68 |
+
}}
|
69 |
+
className="absolute inset-auto left-1/2 h-56 w-[30rem] bg-gradient-conic from-transparent via-transparent to-neutral-500 text-white [--conic-position:from_290deg_at_center_top]"
|
70 |
+
>
|
71 |
+
<div className="absolute w-40 h-[100%] right-0 bg-neutral-950 bottom-0 z-20 [mask-image:linear-gradient(to_left,white,transparent)]" />
|
72 |
+
<div className="absolute w-[100%] right-0 bg-neutral-950 h-40 bottom-0 z-20 [mask-image:linear-gradient(to_top,white,transparent)]" />
|
73 |
+
</motion.div>
|
74 |
+
<div className="absolute top-1/2 h-48 w-full translate-y-12 scale-x-150 bg-neutral-950 blur-2xl"></div>
|
75 |
+
<div className="absolute top-1/2 z-50 h-48 w-full bg-transparent opacity-10 backdrop-blur-md"></div>
|
76 |
+
<div className="absolute inset-auto z-50 h-36 w-[28rem] -translate-y-1/2 rounded-full bg-neutral-500 opacity-50 blur-3xl"></div>
|
77 |
+
<motion.div
|
78 |
+
initial={{ width: "8rem" }}
|
79 |
+
whileInView={{ width: "16rem" }}
|
80 |
+
transition={{
|
81 |
+
delay: 0.3,
|
82 |
+
duration: 0.8,
|
83 |
+
ease: "easeInOut",
|
84 |
+
}}
|
85 |
+
className="absolute inset-auto z-30 h-36 w-64 -translate-y-[6rem] rounded-full bg-neutral-400 blur-2xl"
|
86 |
+
></motion.div>
|
87 |
+
<motion.div
|
88 |
+
initial={{ width: "15rem" }}
|
89 |
+
whileInView={{ width: "30rem" }}
|
90 |
+
transition={{
|
91 |
+
delay: 0.3,
|
92 |
+
duration: 0.8,
|
93 |
+
ease: "easeInOut",
|
94 |
+
}}
|
95 |
+
className="absolute inset-auto z-50 h-0.5 w-[30rem] -translate-y-[7rem] bg-neutral-400 "
|
96 |
+
></motion.div>
|
97 |
+
|
98 |
+
<div className="w-[40rem] h-40 relative">
|
99 |
+
<SparklesCore
|
100 |
+
background="transparent"
|
101 |
+
minSize={0.4}
|
102 |
+
maxSize={1}
|
103 |
+
particleDensity={1200}
|
104 |
+
className="w-full h-full"
|
105 |
+
particleColor="#FFFFFF"
|
106 |
+
/>
|
107 |
+
</div>
|
108 |
+
|
109 |
+
<div className="absolute inset-auto z-40 h-44 w-full -translate-y-[12.5rem] bg-neutral-950 "></div>
|
110 |
+
</div>
|
111 |
+
|
112 |
+
<div className="relative z-50 flex -translate-y-80 flex-col items-center px-5">
|
113 |
+
{children}
|
114 |
+
</div>
|
115 |
+
</div>
|
116 |
+
);
|
117 |
+
};
|