feat: update cube component color scheme from cyan to blue
- Changed cube gradient colors from cyan to blue for better visual consistency - Updated glow effects and shadows to use blue (rgba(59, 130, 246)) instead of cyan - Modified background aura gradients in StackSection for enhanced depth perception - Replaced HomeFeaturesDark component with new HomeSlider in HomePage layout - Added isolate property to StackSection to prevent gradient bleeding - Enhanced background layer in StackSection with additional
This commit is contained in:
@@ -26,11 +26,11 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
|
|||||||
<path
|
<path
|
||||||
fill={`url(#cube-gradient-${index})`}
|
fill={`url(#cube-gradient-${index})`}
|
||||||
d="M491.651 144.747L287.198 227.339C265.219 236.22 241.783 236.22 219.802 227.339L15.3486 144.747C-5.11621 136.479 -5.11621 97.5191 15.3486 89.2539L219.802 6.65884C241.783 -2.21961 265.219 -2.21961 287.198 6.65884L491.651 89.2539C512.116 97.5191 512.116 136.479 491.651 144.747Z"
|
d="M491.651 144.747L287.198 227.339C265.219 236.22 241.783 236.22 219.802 227.339L15.3486 144.747C-5.11621 136.479 -5.11621 97.5191 15.3486 89.2539L219.802 6.65884C241.783 -2.21961 265.219 -2.21961 287.198 6.65884L491.651 89.2539C512.116 97.5191 512.116 136.479 491.651 144.747Z"
|
||||||
stroke="rgba(0,255,255,0.25)"
|
stroke="rgba(59, 130, 246, 0.25)"
|
||||||
strokeWidth="0.5"
|
strokeWidth="0.5"
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
{/* Cyan-white soft gradient */}
|
{/* Blue-white soft gradient */}
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id={`cube-gradient-${index}`}
|
id={`cube-gradient-${index}`}
|
||||||
x1="185.298"
|
x1="185.298"
|
||||||
@@ -39,8 +39,8 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
|
|||||||
y2="206.448"
|
y2="206.448"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop offset="0%" stopColor="#DFFFFF" stopOpacity="0.75" />
|
<stop offset="0%" stopColor="#EBF8FF" stopOpacity="0.75" />
|
||||||
<stop offset="40%" stopColor="#A5F4FF" stopOpacity="0.8" />
|
<stop offset="40%" stopColor="#BEE3F8" stopOpacity="0.8" />
|
||||||
<stop offset="100%" stopColor="#FFFFFF" stopOpacity="0.9" />
|
<stop offset="100%" stopColor="#FFFFFF" stopOpacity="0.9" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
@@ -79,8 +79,8 @@ export function CubeLight({
|
|||||||
<div
|
<div
|
||||||
className={`absolute inset-0 blur-3xl rounded-2xl transition-all duration-500 ${
|
className={`absolute inset-0 blur-3xl rounded-2xl transition-all duration-500 ${
|
||||||
isActive
|
isActive
|
||||||
? "bg-cyan-400/40 opacity-70"
|
? "bg-blue-400/40 opacity-70"
|
||||||
: "bg-cyan-200/20 opacity-40"
|
: "bg-blue-200/20 opacity-40"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ export function CubeLight({
|
|||||||
className="w-48 sm:w-64 lg:w-80 h-auto relative"
|
className="w-48 sm:w-64 lg:w-80 h-auto relative"
|
||||||
style={{
|
style={{
|
||||||
filter: isActive
|
filter: isActive
|
||||||
? "drop-shadow(0 0 25px rgba(0,255,255,0.4)) brightness(1.1)"
|
? "drop-shadow(0 0 25px rgba(59, 130, 246, 0.4)) brightness(1.1)"
|
||||||
: "drop-shadow(0 0 10px rgba(0,255,255,0.15)) brightness(1)",
|
: "drop-shadow(0 0 10px rgba(59, 130, 246, 0.15)) brightness(1)",
|
||||||
transition: "all 0.4s ease",
|
transition: "all 0.4s ease",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -99,10 +99,10 @@ export function CubeLight({
|
|||||||
{/* Title overlay */}
|
{/* Title overlay */}
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
<h3
|
<h3
|
||||||
className="text-cyan-900 text-sm lg:text-base font-medium text-center px-4"
|
className="text-blue-900 text-sm lg:text-base font-medium text-center px-4"
|
||||||
style={{
|
style={{
|
||||||
textShadow:
|
textShadow:
|
||||||
"0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(0,255,255,0.5)",
|
"0 0 15px rgba(255,255,255,0.8), 0 0 25px rgba(59, 130, 246, 0.5)",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
@@ -131,7 +131,7 @@ export function CubeLight({
|
|||||||
y1="1"
|
y1="1"
|
||||||
x2="120"
|
x2="120"
|
||||||
y2="1"
|
y2="1"
|
||||||
stroke="rgba(0,255,255,0.6)"
|
stroke="rgba(59, 130, 246, 0.6)"
|
||||||
strokeWidth="1"
|
strokeWidth="1"
|
||||||
opacity="0.8"
|
opacity="0.8"
|
||||||
/>
|
/>
|
||||||
|
|||||||
293
src/components/ui/apple-cards-carousel.tsx
Normal file
293
src/components/ui/apple-cards-carousel.tsx
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
"use client";
|
||||||
|
import React, {
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
} from "react";
|
||||||
|
import {
|
||||||
|
IconArrowNarrowLeft,
|
||||||
|
IconArrowNarrowRight,
|
||||||
|
IconX,
|
||||||
|
} from "@tabler/icons-react";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { AnimatePresence, motion } from "motion/react";
|
||||||
|
import { useOutsideClick } from "@/hooks/use-outside-click";
|
||||||
|
|
||||||
|
interface CarouselProps {
|
||||||
|
items: JSX.Element[];
|
||||||
|
initialScroll?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Card = {
|
||||||
|
src: string;
|
||||||
|
title: string;
|
||||||
|
category: string;
|
||||||
|
content: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CarouselContext = createContext<{
|
||||||
|
onCardClose: (index: number) => void;
|
||||||
|
currentIndex: number;
|
||||||
|
}>({
|
||||||
|
onCardClose: () => {},
|
||||||
|
currentIndex: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Carousel = ({ items, initialScroll = 0 }: CarouselProps) => {
|
||||||
|
const carouselRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
const [canScrollLeft, setCanScrollLeft] = React.useState(false);
|
||||||
|
const [canScrollRight, setCanScrollRight] = React.useState(true);
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current.scrollLeft = initialScroll;
|
||||||
|
checkScrollability();
|
||||||
|
}
|
||||||
|
}, [initialScroll]);
|
||||||
|
|
||||||
|
const checkScrollability = () => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;
|
||||||
|
setCanScrollLeft(scrollLeft > 0);
|
||||||
|
setCanScrollRight(scrollLeft < scrollWidth - clientWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollLeft = () => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current.scrollBy({ left: -300, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrollRight = () => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
carouselRef.current.scrollBy({ left: 300, behavior: "smooth" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCardClose = (index: number) => {
|
||||||
|
if (carouselRef.current) {
|
||||||
|
const cardWidth = isMobile() ? 230 : 384; // (md:w-96)
|
||||||
|
const gap = isMobile() ? 4 : 8;
|
||||||
|
const scrollPosition = (cardWidth + gap) * (index + 1);
|
||||||
|
carouselRef.current.scrollTo({
|
||||||
|
left: scrollPosition,
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMobile = () => {
|
||||||
|
return window && window.innerWidth < 768;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CarouselContext.Provider
|
||||||
|
value={{ onCardClose: handleCardClose, currentIndex }}
|
||||||
|
>
|
||||||
|
<div className="relative w-full">
|
||||||
|
<div
|
||||||
|
className="flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 [scrollbar-width:none] md:py-20"
|
||||||
|
ref={carouselRef}
|
||||||
|
onScroll={checkScrollability}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l",
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex flex-row justify-start gap-4 pl-4",
|
||||||
|
"mx-auto max-w-7xl", // remove max-w-4xl if you want the carousel to span the full width of its container
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<motion.div
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
y: 20,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.5,
|
||||||
|
delay: 0.2 * index,
|
||||||
|
ease: "easeOut",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
key={"card" + index}
|
||||||
|
className="rounded-3xl last:pr-[5%] md:last:pr-[33%]"
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mr-10 flex justify-end gap-2">
|
||||||
|
<button
|
||||||
|
className="relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-neutral-800 disabled:opacity-50"
|
||||||
|
onClick={scrollLeft}
|
||||||
|
disabled={!canScrollLeft}
|
||||||
|
>
|
||||||
|
<IconArrowNarrowLeft className="h-6 w-6 text-white" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-neutral-800 disabled:opacity-50"
|
||||||
|
onClick={scrollRight}
|
||||||
|
disabled={!canScrollRight}
|
||||||
|
>
|
||||||
|
<IconArrowNarrowRight className="h-6 w-6 text-white" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CarouselContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Card = ({
|
||||||
|
card,
|
||||||
|
index,
|
||||||
|
layout = false,
|
||||||
|
}: {
|
||||||
|
card: Card;
|
||||||
|
index: number;
|
||||||
|
layout?: boolean;
|
||||||
|
}) => {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const { onCardClose } = useContext(CarouselContext);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function onKeyDown(event: KeyboardEvent) {
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (open) {
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = "auto";
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("keydown", onKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", onKeyDown);
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
useOutsideClick(containerRef, () => handleClose());
|
||||||
|
|
||||||
|
const handleOpen = () => {
|
||||||
|
setOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setOpen(false);
|
||||||
|
onCardClose(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AnimatePresence>
|
||||||
|
{open && (
|
||||||
|
<div className="fixed inset-0 z-50 h-screen overflow-auto">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 h-full w-full bg-black/80 backdrop-blur-lg"
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
ref={containerRef}
|
||||||
|
layoutId={layout ? `card-${card.title}` : undefined}
|
||||||
|
className="relative z-[60] mx-auto my-10 h-fit max-w-5xl rounded-3xl bg-neutral-900 p-4 font-sans md:p-10"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="sticky top-4 right-0 ml-auto flex h-8 w-8 items-center justify-center rounded-full bg-black dark:bg-white"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
|
<IconX className="h-6 w-6 text-neutral-100 dark:text-neutral-900" />
|
||||||
|
</button>
|
||||||
|
<motion.p
|
||||||
|
layoutId={layout ? `category-${card.title}` : undefined}
|
||||||
|
className="text-base font-medium text-black dark:text-white"
|
||||||
|
>
|
||||||
|
{card.category}
|
||||||
|
</motion.p>
|
||||||
|
<motion.p
|
||||||
|
layoutId={layout ? `title-${card.title}` : undefined}
|
||||||
|
className="mt-4 text-2xl font-semibold text-neutral-700 md:text-5xl dark:text-white"
|
||||||
|
>
|
||||||
|
{card.title}
|
||||||
|
</motion.p>
|
||||||
|
<div className="py-10">{card.content}</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
<motion.button
|
||||||
|
layoutId={layout ? `card-${card.title}` : undefined}
|
||||||
|
onClick={handleOpen}
|
||||||
|
className="relative z-10 flex h-60 w-56 flex-col items-start justify-start overflow-hidden rounded-3xl bg-neutral-900 md:h-120 md:w-96"
|
||||||
|
>
|
||||||
|
<div className="pointer-events-none absolute inset-x-0 top-0 z-30 h-full bg-gradient-to-b from-black/50 via-transparent to-transparent" />
|
||||||
|
<div className="relative z-40 p-8">
|
||||||
|
<motion.p
|
||||||
|
layoutId={layout ? `category-${card.category}` : undefined}
|
||||||
|
className="text-left font-sans text-sm font-medium text-white md:text-base"
|
||||||
|
>
|
||||||
|
{card.category}
|
||||||
|
</motion.p>
|
||||||
|
<motion.p
|
||||||
|
layoutId={layout ? `title-${card.title}` : undefined}
|
||||||
|
className="mt-2 max-w-xs text-left font-sans text-xl font-semibold [text-wrap:balance] text-white md:text-3xl"
|
||||||
|
>
|
||||||
|
{card.title}
|
||||||
|
</motion.p>
|
||||||
|
</div>
|
||||||
|
<BlurImage
|
||||||
|
src={card.src}
|
||||||
|
alt={card.title}
|
||||||
|
className="absolute inset-0 z-10 h-full w-full object-cover"
|
||||||
|
/>
|
||||||
|
</motion.button>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BlurImage = ({
|
||||||
|
src,
|
||||||
|
className,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
alt,
|
||||||
|
...rest
|
||||||
|
}: React.ImgHTMLAttributes<HTMLImageElement>) => {
|
||||||
|
const [isLoading, setLoading] = useState(true);
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
className={cn(
|
||||||
|
"h-full w-full transition duration-300",
|
||||||
|
isLoading ? "blur-sm" : "blur-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
onLoad={() => setLoading(false)}
|
||||||
|
src={src as string}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
alt={alt ? alt : "Background of a beautiful view"}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
42
src/components/ui/son-of-a-glitch-text.tsx
Normal file
42
src/components/ui/son-of-a-glitch-text.tsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { type HTMLAttributes, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
interface SonOfAGlitchProps extends HTMLAttributes<HTMLHeadingElement> {
|
||||||
|
text: string;
|
||||||
|
textClassName?: string;
|
||||||
|
glitchClassName?: string;
|
||||||
|
showGlitch?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SonOfAGlitch = ({
|
||||||
|
text,
|
||||||
|
className,
|
||||||
|
textClassName,
|
||||||
|
glitchClassName,
|
||||||
|
showGlitch = true,
|
||||||
|
}: SonOfAGlitchProps) => {
|
||||||
|
const [isGlitching, setIsGlitching] = useState(showGlitch);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsGlitching(showGlitch);
|
||||||
|
}, [showGlitch]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<h1 className={cn("relative font-mono", className)}>
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"absolute top-0 left-0 w-full h-full bg-transparent",
|
||||||
|
isGlitching &&
|
||||||
|
"before:content-[attr(data-text)] before:absolute before:top-0 before:w-full before:h-full before:bg-transparent before:left-[-2px] before:text-red-500 before:overflow-hidden before:animate-glitch-1",
|
||||||
|
isGlitching &&
|
||||||
|
"after:content-[attr(data-text)] after:absolute after:top-0 after:w-full after:h-full after:bg-transparent after:left-[2px] after:text-blue-500 after:overflow-hidden after:animate-glitch-2",
|
||||||
|
glitchClassName,
|
||||||
|
)}
|
||||||
|
data-text={text}
|
||||||
|
></span>
|
||||||
|
<span className={cn(textClassName)}>{text}</span>
|
||||||
|
</h1>
|
||||||
|
);
|
||||||
|
};
|
||||||
23
src/hooks/use-outside-click.ts
Normal file
23
src/hooks/use-outside-click.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
|
export const useOutsideClick = (
|
||||||
|
ref: React.RefObject<HTMLDivElement>,
|
||||||
|
callback: Function
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = (event: any) => {
|
||||||
|
if (!ref.current || ref.current.contains(event.target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", listener);
|
||||||
|
document.addEventListener("touchstart", listener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", listener);
|
||||||
|
document.removeEventListener("touchstart", listener);
|
||||||
|
};
|
||||||
|
}, [ref, callback]);
|
||||||
|
};
|
||||||
24
src/hooks/use-outside-click.tsx
Normal file
24
src/hooks/use-outside-click.tsx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
|
export const useOutsideClick = (
|
||||||
|
ref: React.RefObject<HTMLDivElement>,
|
||||||
|
callback: Function
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const listener = (event: any) => {
|
||||||
|
// DO NOTHING if the element being clicked is the target element or their children
|
||||||
|
if (!ref.current || ref.current.contains(event.target)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", listener);
|
||||||
|
document.addEventListener("touchstart", listener);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener("mousedown", listener);
|
||||||
|
document.removeEventListener("touchstart", listener);
|
||||||
|
};
|
||||||
|
}, [ref, callback]);
|
||||||
|
};
|
||||||
@@ -5,6 +5,7 @@ import { StackSectionLight } from './StackSection'
|
|||||||
import { WorldMap } from './HomeGlobe'
|
import { WorldMap } from './HomeGlobe'
|
||||||
import { HomeBenefits } from './HomeBenefits'
|
import { HomeBenefits } from './HomeBenefits'
|
||||||
import { CallToAction } from './CallToAction'
|
import { CallToAction } from './CallToAction'
|
||||||
|
import { HomeSlider } from './HomeSlider'
|
||||||
|
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
@@ -24,7 +25,7 @@ export default function HomePage() {
|
|||||||
|
|
||||||
|
|
||||||
<AnimatedSection>
|
<AnimatedSection>
|
||||||
<HomeFeaturesDark />
|
<HomeSlider />
|
||||||
</AnimatedSection>
|
</AnimatedSection>
|
||||||
|
|
||||||
<AnimatedSection>
|
<AnimatedSection>
|
||||||
|
|||||||
84
src/pages/home/HomeSlider.tsx
Normal file
84
src/pages/home/HomeSlider.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { Carousel, Card } from "@/components/ui/apple-cards-carousel";
|
||||||
|
import { H2, H3, P } from "@/components/Texts";
|
||||||
|
|
||||||
|
export function HomeSlider() {
|
||||||
|
const cards = data.map((card, index) => (
|
||||||
|
<Card key={card.src} card={card} index={index} />
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full py-20 bg-black">
|
||||||
|
<div className="max-w-7xl mx-auto pl-4">
|
||||||
|
<H3 className="text-left text-white">
|
||||||
|
Discover the Mycelium Ecosystem
|
||||||
|
</H3>
|
||||||
|
<div className="mt-4 max-w-3xl">
|
||||||
|
<P className="text-left text-neutral-400">
|
||||||
|
From compute and networking to intelligent automation, these components work together to empower users, developers, and organizations to build freely, without intermediaries.
|
||||||
|
</P>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Carousel items={cards} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DummyContent = () => {
|
||||||
|
return (
|
||||||
|
<div className="bg-neutral-800 p-8 md:p-14 rounded-3xl mb-4 max-w-3xl">
|
||||||
|
<P className="text-neutral-400 text-base md:text-2xl font-sans mx-auto">
|
||||||
|
Mycelium is a decentralized compute network that allows you to run AI models and other software on a global network of devices. Mycelium is built on top of the Mycelium Network, a decentralized network of devices that are connected to each other and to the internet.
|
||||||
|
</P>
|
||||||
|
<img
|
||||||
|
src="/images/gallery/1.webp"
|
||||||
|
alt="Macbook mockup from Aceternity UI"
|
||||||
|
height="500"
|
||||||
|
width="500"
|
||||||
|
className="md:w-1/2 md:h-1/2 h-full w-full mx-auto object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
category: "DePIN",
|
||||||
|
title: "Mycelium Network",
|
||||||
|
src: "/images/gallery/9.webp",
|
||||||
|
content: <DummyContent />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "AI Agent",
|
||||||
|
title: "Mycelium Agent",
|
||||||
|
src: "/images/gallery/2.webp",
|
||||||
|
content: <DummyContent />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Cloud",
|
||||||
|
title: "Mycelium Cloud",
|
||||||
|
src: "/images/gallery/3.webp",
|
||||||
|
content: <DummyContent />,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
category: "GPU",
|
||||||
|
title: "Mycelium GPU",
|
||||||
|
src: "/images/gallery/4.webp",
|
||||||
|
content: <DummyContent />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Compute",
|
||||||
|
title: "Mycelium Compute",
|
||||||
|
src: "/images/gallery/5.webp",
|
||||||
|
content: <DummyContent />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
category: "Storage",
|
||||||
|
title: "Mycelium Storage",
|
||||||
|
src: "/images/gallery/6.webp",
|
||||||
|
content: <DummyContent />,
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -7,35 +7,53 @@ import { FadeIn } from "@/components/ui/FadeIn";
|
|||||||
|
|
||||||
export function StackSectionLight() {
|
export function StackSectionLight() {
|
||||||
return (
|
return (
|
||||||
<section className="relative w-full overflow-hidden py-24 lg:py-40">
|
<section className="relative w-full overflow-hidden py-24 lg:py-40 isolate">
|
||||||
{/* === Background Layer === */}
|
{/* === Background Layer === */}
|
||||||
<div className="absolute inset-0 -z-10 bg-transparent">
|
<div className="absolute inset-0 z-0 bg-transparent">
|
||||||
|
{/* Central main aura */}
|
||||||
|
|
||||||
{/* === Center Radial Glow Aura === */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[1200px] h-[1200px] rounded-full pointer-events-none"
|
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 w-[1200px] h-[1200px] rounded-full pointer-events-none"
|
||||||
style={{
|
style={{
|
||||||
background:
|
background:
|
||||||
"radial-gradient(circle, rgba(173,255,255,0.5) 0%, rgba(0,220,255,0.3) 30%, rgba(255,255,255,0) 70%)",
|
"radial-gradient(circle, rgba(180,255,255,0.55) 0%, rgba(0,210,255,0.35) 35%, rgba(255,255,255,0) 55%)",
|
||||||
filter: "blur(120px)",
|
filter: "blur(140px)",
|
||||||
}}
|
}}
|
||||||
animate={{
|
animate={{
|
||||||
opacity: [0.6, 0.8, 0.6],
|
opacity: [0.5, 0.8, 0.5],
|
||||||
scale: [1, 1.05, 1],
|
scale: [1, 1.05, 1],
|
||||||
}}
|
}}
|
||||||
transition={{
|
transition={{
|
||||||
duration: 8,
|
duration: 9,
|
||||||
repeat: Infinity,
|
repeat: Infinity,
|
||||||
ease: "easeInOut",
|
ease: "easeInOut",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* Faint cyan mist in the back for depth */}
|
||||||
|
<motion.div
|
||||||
|
className="absolute left-[70%] top-[30%] -translate-x-1/2 -translate-y-1/2 w-[1600px] h-[1600px] rounded-full pointer-events-none"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
"radial-gradient(circle, rgba(100,220,255,0.25) 0%, rgba(200,255,255,0.15) 50%, rgba(255,255,255,0) 90%)",
|
||||||
|
filter: "blur(200px)",
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
opacity: [0.2, 0.35, 0.2],
|
||||||
|
scale: [1, 1.1, 1],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 12,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: "easeInOut",
|
||||||
|
delay: 3,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* === Content === */}
|
{/* === Content === */}
|
||||||
<div className="relative mx-auto max-w-7xl px-6 lg:px-8 grid grid-cols-1 lg:grid-cols-3 gap-16 items-center">
|
<div className="relative mx-auto max-w-7xl px-6 lg:px-8 grid grid-cols-1 lg:grid-cols-3 gap-16 items-center">
|
||||||
{/* Left Column - Text */}
|
{/* Left Column - Text */}
|
||||||
<div className="text-center lg:text-left">
|
<div className="text-center lg:text-left z-10">
|
||||||
<FadeIn>
|
<FadeIn>
|
||||||
<Eyebrow color="accent">Technology Layers</Eyebrow>
|
<Eyebrow color="accent">Technology Layers</Eyebrow>
|
||||||
<SectionHeader color="dark" className="text-4xl sm:text-5xl font-semibold">
|
<SectionHeader color="dark" className="text-4xl sm:text-5xl font-semibold">
|
||||||
@@ -53,7 +71,7 @@ export function StackSectionLight() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Column - Animated Stack */}
|
{/* Right Column - Animated Stack */}
|
||||||
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start relative">
|
<div className="lg:col-span-2 flex items-center justify-center lg:justify-start relative z-10">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 30, opacity: 0 }}
|
initial={{ y: 30, opacity: 0 }}
|
||||||
whileInView={{ y: 0, opacity: 1 }}
|
whileInView={{ y: 0, opacity: 1 }}
|
||||||
|
|||||||
32
tailwind.config.js
Normal file
32
tailwind.config.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
keyframes: {
|
||||||
|
'glitch-1': {
|
||||||
|
'0%': { transform: 'none' },
|
||||||
|
'25%': { transform: 'skew(-0.5deg, -0.5deg)' },
|
||||||
|
'50%': { transform: 'none' },
|
||||||
|
'75%': { transform: 'skew(0.5deg, 0.5deg)' },
|
||||||
|
'100%': { transform: 'none' },
|
||||||
|
},
|
||||||
|
'glitch-2': {
|
||||||
|
'0%': { transform: 'none' },
|
||||||
|
'25%': { transform: 'skew(-0.5deg, -0.5deg)' },
|
||||||
|
'50%': { transform: 'none' },
|
||||||
|
'75%': { transform: 'skew(0.5deg, 0.5deg)' },
|
||||||
|
'100%': { transform: 'none' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
'glitch-1': 'glitch-1 1s infinite',
|
||||||
|
'glitch-2': 'glitch-2 1s infinite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user