Creator Card

Make Creator Card and showcase creativity.

Live Preview
Open in

Code

/* eslint-disable @typescript-eslint/no-unused-vars */
"use client"

import type React from "react"

import { useState, useEffect } from "react"
import { motion, AnimatePresence } from "framer-motion"
import { Video, Phone, MessageSquare, MessageCircle, Settings, Star, Heart, Sun, Moon } from "lucide-react"
import Image from "next/image"
import { cn } from "@/lib/utils"
import { useAnimationControls } from "@/hooks/use-animation-controls"
import { useTheme } from "next-themes"

// Types
interface Creator {
  id: string
  name: string
  isVerified?: boolean
  title: string
  avatar: string
  rating: number
  ratingCount: number
  isFavorite?: boolean
}

// Color palette
const colors = {
  primary: {
    light: "bg-amber-500",
    dark: "bg-amber-600",
    text: "text-amber-500",
    fill: "fill-amber-500",
  },
  secondary: {
    light: "bg-orange-400",
    dark: "bg-orange-500",
    text: "text-orange-400",
    fill: "fill-orange-400",
  },
  accent: {
    light: "bg-red-500",
    dark: "bg-red-600",
    text: "text-red-500",
    fill: "fill-red-500",
  },
  background: {
    light: "bg-white",
    dark: "bg-gray-900",
  },
  card: {
    light: "bg-amber-50",
    dark: "bg-gray-800",
  },
  text: {
    primary: {
      light: "text-gray-900",
      dark: "text-white",
    },
    secondary: {
      light: "text-gray-600",
      dark: "text-gray-300",
    },
  },
  button: {
    active: {
      light: "bg-amber-500",
      dark: "bg-amber-600",
    },
    inactive: {
      light: "bg-amber-100",
      dark: "bg-gray-700",
    },
  },
}

// Theme toggle component
function ThemeToggle() {
  const { theme, setTheme } = useTheme()
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) return null

  return (
    <motion.button
      whileHover={{ scale: 1.1 }}
      whileTap={{ scale: 0.9 }}
      onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      className="absolute right-4 top-4 z-10 p-2 rounded-full bg-amber-100 dark:bg-gray-700"
      aria-label="Toggle theme"
    >
      <AnimatePresence mode="wait">
        <motion.div
          key={theme === "dark" ? "dark" : "light"}
          initial={{ scale: 0.8, opacity: 0, rotate: -180 }}
          animate={{ scale: 1, opacity: 1, rotate: 0 }}
          exit={{ scale: 0.8, opacity: 0, rotate: 180 }}
          transition={{ type: "spring", stiffness: 500, damping: 30 }}
        >
          {theme === "dark" ? <Sun className="w-5 h-5 text-amber-500" /> : <Moon className="w-5 h-5 text-amber-600" />}
        </motion.div>
      </AnimatePresence>
    </motion.button>
  )
}

// Sub-components
function ActionButton({
  icon,
  label,
  isActive = false,
  onClick,
}: {
  icon: React.ReactNode
  label: string
  isActive?: boolean
  onClick?: () => void
}) {
  return (
    <motion.button
      whileHover={{ scale: 1.05 }}
      whileTap={{ scale: 0.95 }}
      className={cn(
        "flex items-center justify-center w-12 h-12 rounded-full",
        isActive
          ? "bg-amber-500 dark:bg-amber-600 text-white"
          : "bg-amber-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200",
      )}
      onClick={onClick}
      aria-label={label}
      initial={{ opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{
        type: "spring",
        stiffness: 300,
        damping: 20,
      }}
    >
      {icon}
    </motion.button>
  )
}

function AvatarWithStatus({
  src,
  alt,
  size = 64,
}: {
  src: string
  alt: string
  size?: number
}) {
  return (
    <motion.div
      className="relative"
      initial={{ scale: 0.8, opacity: 0 }}
      animate={{ scale: 1, opacity: 1 }}
      transition={{
        type: "spring",
        stiffness: 300,
        damping: 20,
        delay: 0.1,
      }}
    >
      <div className="rounded-2xl overflow-hidden bg-amber-100 dark:bg-amber-200 shadow-md">
        <Image src={src || "/placeholder.svg"} alt={alt} width={size} height={size} className="object-cover" />
      </div>
    </motion.div>
  )
}

function Rating({
  value,
  count,
}: {
  value: number
  count: number
}) {
  return (
    <motion.div
      className="flex items-center gap-1 text-sm"
      initial={{ opacity: 0, x: -10 }}
      animate={{ opacity: 1, x: 0 }}
      transition={{ delay: 0.3 }}
    >
      <Star className="w-5 h-5 fill-amber-500 text-amber-500 dark:fill-amber-400 dark:text-amber-400" />
      <span className="text-gray-800 dark:text-white">
        {value} ({count})
      </span>
    </motion.div>
  )
}

function FavoriteButton({
  initialState = false,
  onToggle,
}: {
  initialState?: boolean
  onToggle?: (state: boolean) => void
}) {
  const [isFavorite, setIsFavorite] = useState(initialState)

  const handleToggle = () => {
    const newState = !isFavorite
    setIsFavorite(newState)
    if (onToggle) onToggle(newState)
  }

  return (
    <motion.button
      whileHover={{ scale: 1.1 }}
      whileTap={{ scale: 0.9 }}
      onClick={handleToggle}
      className="absolute right-4 top-4"
      aria-label={isFavorite ? "Remove from favorites" : "Add to favorites"}
    >
      <AnimatePresence mode="wait">
        <motion.div
          key={isFavorite ? "filled" : "outline"}
          initial={{ scale: 0.8, opacity: 0 }}
          animate={{ scale: 1, opacity: 1 }}
          exit={{ scale: 0.8, opacity: 0 }}
          transition={{ type: "spring", stiffness: 500, damping: 30 }}
        >
          {isFavorite ? (
            <Heart className="w-6 h-6 fill-red-500 text-red-500 dark:fill-red-400 dark:text-red-400" />
          ) : (
            <Heart className="w-6 h-6 text-red-500 dark:text-red-400" />
          )}
        </motion.div>
      </AnimatePresence>
    </motion.button>
  )
}

// Main component
export function CreatorCard({ creator }: { creator: Creator }) {
  const [activeAction, setActiveAction] = useState<string>("video")
  const { scope, handleMouseEnter, handleMouseLeave } = useAnimationControls()
  const { resolvedTheme } = useTheme()
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
  }, [])

  const actions = [
    { id: "video", icon: <Video className="w-5 h-5" />, label: "Video call" },
    { id: "call", icon: <Phone className="w-5 h-5" />, label: "Call" },
    { id: "message", icon: <MessageSquare className="w-5 h-5" />, label: "Message" },
    { id: "chat", icon: <MessageCircle className="w-5 h-5" />, label: "Chat" },
    { id: "settings", icon: <Settings className="w-5 h-5" />, label: "Settings" },
  ]

  const handleActionClick = (id: string) => {
    setActiveAction(id)
  }

  const container = {
    hidden: { opacity: 0 },
    show: {
      opacity: 1,
      transition: {
        staggerChildren: 0.05,
        delayChildren: 0.3,
      },
    },
  }

  const item = {
    hidden: { opacity: 0, y: 20 },
    show: { opacity: 1, y: 0, transition: { type: "spring", stiffness: 300, damping: 20 } },
  }

  // Don't render until mounted to avoid hydration mismatch
  if (!mounted) {
    return null
  }

  return (
    <motion.div
      ref={scope}
      className="relative max-w-xs mx-auto"
      initial={{ y: 50, opacity: 0 }}
      animate={{ y: 0, opacity: 1 }}
      transition={{ type: "spring", stiffness: 300, damping: 20 }}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      <motion.div
        className="rounded-3xl overflow-hidden shadow-xl bg-gradient-to-br from-amber-50 to-orange-50 dark:from-gray-800 dark:to-gray-900"
        layoutId={`creator-card-${creator.id}`}
      >
        <div className="p-4">
          <div className="flex items-center gap-3">
            <AvatarWithStatus src={creator.avatar} alt={creator.name} />
            <div className="flex-1">
              <motion.div
                className="flex items-center gap-1"
                initial={{ opacity: 0, y: -10 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ delay: 0.2 }}
              >
                <h3 className="text-gray-900 dark:text-white text-lg font-medium">{creator.name}</h3>
                {creator.isVerified && <span className="text-orange-500 dark:text-orange-400 text-lg">🎮</span>}
              </motion.div>
              <motion.p
                className="text-gray-600 dark:text-gray-300 text-sm"
                initial={{ opacity: 0, y: -5 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ delay: 0.25 }}
              >
                {creator.title}
              </motion.p>
              <Rating value={creator.rating} count={creator.ratingCount} />
            </div>
            <FavoriteButton initialState={creator.isFavorite} />
          </div>
        </div>

        <motion.div
          className="flex justify-between px-4 py-3 gap-2 bg-amber-50/80 dark:bg-gray-800/80 backdrop-blur-sm"
          variants={container}
          initial="hidden"
          animate="show"
        >
          {actions.map((action) => (
            <motion.div key={action.id} variants={item}>
              <ActionButton
                icon={action.icon}
                label={action.label}
                isActive={activeAction === action.id}
                onClick={() => handleActionClick(action.id)}
              />
            </motion.div>
          ))}
        </motion.div>
      </motion.div>
    </motion.div>
  )
}

// Demo page component
export function CreatorCardDemo() {
  const creator = {
    id: "1",
    name: "Karan",
    isVerified: true,
    title: "Founder SwiftUI⚡",
    avatar: "/favicon-2.png?height=64&width=64",
    rating: 5,
    ratingCount: 35,
    isFavorite: false,
  }

  return (
    <div className="w-full max-w-md">
      <ThemeToggle />
      <CreatorCard creator={creator} />
    </div>
  )
}

You can chnage details in creator initialization