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
|
||||
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"
|
||||
stroke="rgba(0,255,255,0.25)"
|
||||
stroke="rgba(59, 130, 246, 0.25)"
|
||||
strokeWidth="0.5"
|
||||
/>
|
||||
<defs>
|
||||
{/* Cyan-white soft gradient */}
|
||||
{/* Blue-white soft gradient */}
|
||||
<linearGradient
|
||||
id={`cube-gradient-${index}`}
|
||||
x1="185.298"
|
||||
@@ -39,8 +39,8 @@ const CubeSvg: React.FC<React.SVGProps<SVGSVGElement> & { index: number }> = ({
|
||||
y2="206.448"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0%" stopColor="#DFFFFF" stopOpacity="0.75" />
|
||||
<stop offset="40%" stopColor="#A5F4FF" stopOpacity="0.8" />
|
||||
<stop offset="0%" stopColor="#EBF8FF" stopOpacity="0.75" />
|
||||
<stop offset="40%" stopColor="#BEE3F8" stopOpacity="0.8" />
|
||||
<stop offset="100%" stopColor="#FFFFFF" stopOpacity="0.9" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
@@ -79,8 +79,8 @@ export function CubeLight({
|
||||
<div
|
||||
className={`absolute inset-0 blur-3xl rounded-2xl transition-all duration-500 ${
|
||||
isActive
|
||||
? "bg-cyan-400/40 opacity-70"
|
||||
: "bg-cyan-200/20 opacity-40"
|
||||
? "bg-blue-400/40 opacity-70"
|
||||
: "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"
|
||||
style={{
|
||||
filter: isActive
|
||||
? "drop-shadow(0 0 25px rgba(0,255,255,0.4)) brightness(1.1)"
|
||||
: "drop-shadow(0 0 10px rgba(0,255,255,0.15)) brightness(1)",
|
||||
? "drop-shadow(0 0 25px rgba(59, 130, 246, 0.4)) brightness(1.1)"
|
||||
: "drop-shadow(0 0 10px rgba(59, 130, 246, 0.15)) brightness(1)",
|
||||
transition: "all 0.4s ease",
|
||||
}}
|
||||
/>
|
||||
@@ -99,10 +99,10 @@ export function CubeLight({
|
||||
{/* Title overlay */}
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<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={{
|
||||
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}
|
||||
@@ -131,7 +131,7 @@ export function CubeLight({
|
||||
y1="1"
|
||||
x2="120"
|
||||
y2="1"
|
||||
stroke="rgba(0,255,255,0.6)"
|
||||
stroke="rgba(59, 130, 246, 0.6)"
|
||||
strokeWidth="1"
|
||||
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 { HomeBenefits } from './HomeBenefits'
|
||||
import { CallToAction } from './CallToAction'
|
||||
import { HomeSlider } from './HomeSlider'
|
||||
|
||||
|
||||
export default function HomePage() {
|
||||
@@ -24,7 +25,7 @@ export default function HomePage() {
|
||||
|
||||
|
||||
<AnimatedSection>
|
||||
<HomeFeaturesDark />
|
||||
<HomeSlider />
|
||||
</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() {
|
||||
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 === */}
|
||||
<div className="absolute inset-0 -z-10 bg-transparent">
|
||||
|
||||
|
||||
{/* === Center Radial Glow Aura === */}
|
||||
<div className="absolute inset-0 z-0 bg-transparent">
|
||||
{/* Central main aura */}
|
||||
<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"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(circle, rgba(173,255,255,0.5) 0%, rgba(0,220,255,0.3) 30%, rgba(255,255,255,0) 70%)",
|
||||
filter: "blur(120px)",
|
||||
"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(140px)",
|
||||
}}
|
||||
animate={{
|
||||
opacity: [0.6, 0.8, 0.6],
|
||||
opacity: [0.5, 0.8, 0.5],
|
||||
scale: [1, 1.05, 1],
|
||||
}}
|
||||
transition={{
|
||||
duration: 8,
|
||||
duration: 9,
|
||||
repeat: Infinity,
|
||||
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>
|
||||
|
||||
{/* === 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">
|
||||
{/* Left Column - Text */}
|
||||
<div className="text-center lg:text-left">
|
||||
<div className="text-center lg:text-left z-10">
|
||||
<FadeIn>
|
||||
<Eyebrow color="accent">Technology Layers</Eyebrow>
|
||||
<SectionHeader color="dark" className="text-4xl sm:text-5xl font-semibold">
|
||||
@@ -53,7 +71,7 @@ export function StackSectionLight() {
|
||||
</div>
|
||||
|
||||
{/* 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
|
||||
initial={{ y: 30, opacity: 0 }}
|
||||
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