feat: replace card stack with new feature grid and add product icons
This commit is contained in:
		@@ -1,48 +0,0 @@
 | 
			
		||||
"use client";
 | 
			
		||||
import { motion } from "framer-motion";
 | 
			
		||||
 | 
			
		||||
type Card = {
 | 
			
		||||
  id: number;
 | 
			
		||||
  name: string;
 | 
			
		||||
  description: string;
 | 
			
		||||
  icon: React.ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CardStack = ({
 | 
			
		||||
  items,
 | 
			
		||||
  offset,
 | 
			
		||||
  scaleFactor,
 | 
			
		||||
}: {
 | 
			
		||||
  items: Card[];
 | 
			
		||||
  offset?: number;
 | 
			
		||||
  scaleFactor?: number;
 | 
			
		||||
}) => {
 | 
			
		||||
  const CARD_OFFSET = offset || 10;
 | 
			
		||||
  const HORIZONTAL_OFFSET = 336; // Adjusted for 1/8 overlap
 | 
			
		||||
  const SCALE_FACTOR = scaleFactor || 0.06;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="relative h-[20rem] w-full flex items-center justify-center">
 | 
			
		||||
      {items.map((card, index) => (
 | 
			
		||||
        <motion.div
 | 
			
		||||
          key={card.id}
 | 
			
		||||
          className="absolute dark:bg-black bg-white h-[16rem] w-[24rem] rounded-3xl p-4 shadow-xl border border-neutral-200 dark:border-white/[0.1] shadow-black/[0.1] dark:shadow-white/[0.05] flex flex-col justify-between"
 | 
			
		||||
          style={{
 | 
			
		||||
            transformOrigin: "top center",
 | 
			
		||||
          }}
 | 
			
		||||
          animate={{
 | 
			
		||||
            top: index * -CARD_OFFSET,
 | 
			
		||||
            left: index * HORIZONTAL_OFFSET,
 | 
			
		||||
            scale: 1 - index * SCALE_FACTOR,
 | 
			
		||||
            zIndex: items.length - index,
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <div className="flex flex-col p-4">
 | 
			
		||||
            <h3 className="text-base/7 font-semibold text-gray-900 dark:text-white">{card.name}</h3>
 | 
			
		||||
            <p className="mt-1 flex-auto text-base/7 text-gray-600 dark:text-neutral-300">{card.description}</p>
 | 
			
		||||
          </div>
 | 
			
		||||
        </motion.div>
 | 
			
		||||
      ))}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										158
									
								
								src/components/ui/glowing-stars.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								src/components/ui/glowing-stars.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,158 @@
 | 
			
		||||
"use client";
 | 
			
		||||
 | 
			
		||||
import React, { useEffect, useRef, useState } from "react";
 | 
			
		||||
import { AnimatePresence, motion } from "motion/react";
 | 
			
		||||
import { cn } from "@/lib/utils";
 | 
			
		||||
 | 
			
		||||
export const GlowingStarsBackgroundCard = ({
 | 
			
		||||
  className,
 | 
			
		||||
  children,
 | 
			
		||||
}: {
 | 
			
		||||
  className?: string;
 | 
			
		||||
  children?: React.ReactNode;
 | 
			
		||||
}) => {
 | 
			
		||||
  const [mouseEnter, setMouseEnter] = useState(false);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      onMouseEnter={() => {
 | 
			
		||||
        setMouseEnter(true);
 | 
			
		||||
      }}
 | 
			
		||||
      onMouseLeave={() => {
 | 
			
		||||
        setMouseEnter(false);
 | 
			
		||||
      }}
 | 
			
		||||
      className={cn(
 | 
			
		||||
        "bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 max-w-md max-h-[20rem] h-full w-full rounded-xl border border-[#eaeaea] dark:border-neutral-600",
 | 
			
		||||
        className
 | 
			
		||||
      )}
 | 
			
		||||
    >
 | 
			
		||||
      <div className="flex justify-center items-center">
 | 
			
		||||
        <Illustration mouseEnter={mouseEnter} />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="px-2 pb-6">{children}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const GlowingStarsDescription = ({
 | 
			
		||||
  className,
 | 
			
		||||
  children,
 | 
			
		||||
}: {
 | 
			
		||||
  className?: string;
 | 
			
		||||
  children?: React.ReactNode;
 | 
			
		||||
}) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <p className={cn("text-base text-white max-w-[16rem]", className)}>
 | 
			
		||||
      {children}
 | 
			
		||||
    </p>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const GlowingStarsTitle = ({
 | 
			
		||||
  className,
 | 
			
		||||
  children,
 | 
			
		||||
}: {
 | 
			
		||||
  className?: string;
 | 
			
		||||
  children?: React.ReactNode;
 | 
			
		||||
}) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <h2 className={cn("font-bold text-2xl text-[#eaeaea]", className)}>
 | 
			
		||||
      {children}
 | 
			
		||||
    </h2>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const Illustration = ({ mouseEnter }: { mouseEnter: boolean }) => {
 | 
			
		||||
  const stars = 108;
 | 
			
		||||
  const columns = 18;
 | 
			
		||||
 | 
			
		||||
  const [glowingStars, setGlowingStars] = useState<number[]>([]);
 | 
			
		||||
 | 
			
		||||
  const highlightedStars = useRef<number[]>([]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const interval = setInterval(() => {
 | 
			
		||||
      highlightedStars.current = Array.from({ length: 5 }, () =>
 | 
			
		||||
        Math.floor(Math.random() * stars)
 | 
			
		||||
      );
 | 
			
		||||
      setGlowingStars([...highlightedStars.current]);
 | 
			
		||||
    }, 3000);
 | 
			
		||||
 | 
			
		||||
    return () => clearInterval(interval);
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className="h-48 p-1 w-full"
 | 
			
		||||
      style={{
 | 
			
		||||
        display: "grid",
 | 
			
		||||
        gridTemplateColumns: `repeat(${columns}, 1fr)`,
 | 
			
		||||
        gap: `1px`,
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      {[...Array(stars)].map((_, starIdx) => {
 | 
			
		||||
        const isGlowing = glowingStars.includes(starIdx);
 | 
			
		||||
        const delay = (starIdx % 10) * 0.1;
 | 
			
		||||
        const staticDelay = starIdx * 0.01;
 | 
			
		||||
        return (
 | 
			
		||||
          <div
 | 
			
		||||
            key={`matrix-col-${starIdx}}`}
 | 
			
		||||
            className="relative flex items-center justify-center"
 | 
			
		||||
          >
 | 
			
		||||
            <Star
 | 
			
		||||
              isGlowing={mouseEnter ? true : isGlowing}
 | 
			
		||||
              delay={mouseEnter ? staticDelay : delay}
 | 
			
		||||
            />
 | 
			
		||||
            {mouseEnter && <Glow delay={staticDelay} />}
 | 
			
		||||
            <AnimatePresence mode="wait">
 | 
			
		||||
              {isGlowing && <Glow delay={delay} />}
 | 
			
		||||
            </AnimatePresence>
 | 
			
		||||
          </div>
 | 
			
		||||
        );
 | 
			
		||||
      })}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Star = ({ isGlowing, delay }: { isGlowing: boolean; delay: number }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <motion.div
 | 
			
		||||
      key={delay}
 | 
			
		||||
      initial={{
 | 
			
		||||
        scale: 1,
 | 
			
		||||
      }}
 | 
			
		||||
      animate={{
 | 
			
		||||
        scale: isGlowing ? [1, 1.2, 2.5, 2.2, 1.5] : 1,
 | 
			
		||||
        background: isGlowing ? "#fff" : "#666",
 | 
			
		||||
      }}
 | 
			
		||||
      transition={{
 | 
			
		||||
        duration: 2,
 | 
			
		||||
        ease: "easeInOut",
 | 
			
		||||
        delay: delay,
 | 
			
		||||
      }}
 | 
			
		||||
      className={cn("bg-[#666] h-[1px] w-[1px] rounded-full relative z-20")}
 | 
			
		||||
    ></motion.div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Glow = ({ delay }: { delay: number }) => {
 | 
			
		||||
  return (
 | 
			
		||||
    <motion.div
 | 
			
		||||
      initial={{
 | 
			
		||||
        opacity: 0,
 | 
			
		||||
      }}
 | 
			
		||||
      animate={{
 | 
			
		||||
        opacity: 1,
 | 
			
		||||
      }}
 | 
			
		||||
      transition={{
 | 
			
		||||
        duration: 2,
 | 
			
		||||
        ease: "easeInOut",
 | 
			
		||||
        delay: delay,
 | 
			
		||||
      }}
 | 
			
		||||
      exit={{
 | 
			
		||||
        opacity: 0,
 | 
			
		||||
      }}
 | 
			
		||||
      className="absolute  left-1/2 -translate-x-1/2 z-10 h-[4px] w-[4px] rounded-full bg-blue-500 blur-[1px] shadow-2xl shadow-blue-400"
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user