Aesthe UI

Pro Card

A premium profile card component with 3D hover effects, interactive animations, and multiple layout variants

Overview

The ProCard is a premium profile card component with 3D hover effects, interactive animations, and multiple layout variants. Perfect for showcasing user profiles, team members, or personal information with a modern, engaging interface.

Features

  • 3D Hover Effects - Smooth tilt animation with perspective
  • Interactive Animations - Shimmer effect on click and mouse-following glare
  • Multiple Variants - Default, compact, and elegant layouts
  • Light/Dark Mode - Fully adaptive styling for both themes
  • Avatar Support - Next.js Image optimization for profile pictures
  • Tech Stack Display - Badges for technologies and cloud services
  • Contact Information - Email, location, and date with icons
  • Custom Content - Support for custom children layouts

Installation

pro-card.tsx

'use client'
import { useRef, useState, MouseEvent } from 'react'
import { cn } from '@/lib/utils'
import Image from 'next/image'
import { MapPin, Mail, Code, Cloud } from 'lucide-react'
import { Calendar } from 'lucide-react'

interface ProCardProps {
  avatar?: string
  email?: string
  location?: string
  tech?: string[]
  cloud?: string[]
  name?: string
  title?: string
  date?: string
  variant?: 'default' | 'compact' | 'elegant'
  children?: React.ReactNode
  className?: string
}

export const ProCard = ({
  avatar,
  email,
  location,
  tech = [],
  cloud = [],
  name,
  title,
  date,
  variant = 'default',
  children,
  className,
}: ProCardProps) => {
  const cardRef = useRef<HTMLDivElement>(null)
  const [transform, setTransform] = useState('')
  const [isGlaring, setIsGlaring] = useState(false)
  const [glarePosition, setGlarePosition] = useState({ x: 0, y: 0 })

  const handleMouseMove = (e: MouseEvent<HTMLDivElement>) => {
    if (!cardRef.current) return

    const card = cardRef.current
    const rect = card.getBoundingClientRect()
    const x = e.clientX - rect.left
    const y = e.clientY - rect.top

    const centerX = rect.width / 2
    const centerY = rect.height / 2

    const rotateX = ((y - centerY) / centerY) * -10
    const rotateY = ((x - centerX) / centerX) * 10

    setTransform(
      `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.02, 1.02, 1.02)`
    )

    setGlarePosition({ x, y })
  }

  const handleMouseLeave = () => {
    setTransform('perspective(1000px) rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)')
  }

  const handleClick = () => {
    setIsGlaring(true)
    setTimeout(() => setIsGlaring(false), 800)
  }

  return (
    <div className="perspective-1000">
      <div
        ref={cardRef}
        onMouseMove={handleMouseMove}
        onMouseLeave={handleMouseLeave}
        onClick={handleClick}
        className={cn(
          'group relative cursor-pointer overflow-hidden rounded-none bg-zinc shadow-zinc-950/5 transition-all duration-300 ease-out',
          'border border-zinc-200 dark:border-zinc-800',
          'hover:shadow-2xl',
          className
        )}
        style={{
          transform,
          transition: 'transform 0.1s ease-out, box-shadow 0.3s ease-out',
        }}
      >
        {/* Background matching FeatureCard exactly */}
        <div className="absolute inset-0 bg-zinc-50 dark:bg-transparent" />
        
        {/* Corner Borders - Exact same as FeatureCard */}
        <span className="border-primary absolute -left-px -top-px block size-2 border-l-2 border-t-2"></span>
        <span className="border-primary absolute -right-px -top-px block size-2 border-r-2 border-t-2"></span>
        <span className="border-primary absolute -bottom-px -left-px block size-2 border-b-2 border-l-2"></span>
        <span className="border-primary absolute -bottom-px -right-px block size-2 border-b-2 border-r-2"></span>

        {/* Shimmer overlay on click */}
        {isGlaring && <div className="dark:shimmer-effect pointer-events-none absolute inset-0" />}

        {/* Glare effect that follows mouse */}
        <div
          className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300 hover:opacity-100"
          style={{
            background: `radial-gradient(circle 200px at ${glarePosition.x}px ${glarePosition.y}px, hsl(var(--glare) / 0.15), transparent)`,
          }}
        />

        {/* Moving glare on click */}
        {isGlaring && (
          <div className="glare-effect pointer-events-none absolute inset-0">
            <div className="absolute top-0 left-0 h-full w-full -skew-x-12 bg-gradient-to-r from-transparent via-white/30 to-transparent" />
          </div>
        )}

        {/* Content */}
        <div className="relative z-10 p-6">
          {children ? (
            children
          ) : variant === 'elegant' ? (
            <div className="flex flex-col items-center gap-8 md:flex-row">
              {/* Avatar */}
              <div className="shrink-0">
                {avatar ? (
                  <div className="relative h-32 w-32 overflow-hidden rounded-full shadow-xl ring-2 ring-white/30 md:h-40 md:w-40">
                    <Image src={avatar} alt={name || 'Profile'} fill className="object-cover" />
                  </div>
                ) : (
                  <div className="from-primary/20 to-secondary/20 h-32 w-32 rounded-full border-2 border-white/30 bg-gradient-to-br shadow-xl backdrop-blur-sm md:h-40 md:w-40" />
                )}
              </div>

              {/* Content */}
              <div className="flex-1 space-y-4 text-center md:text-left">
                <div>
                  {name && (
                    <h1 className="from-primary via-secondary to-accent mb-2 bg-gradient-to-r bg-clip-text text-4xl font-bold text-transparent md:text-5xl">
                      {name}
                    </h1>
                  )}
                  {title && <p className="text-muted-foreground text-lg font-medium">{title}</p>}
                </div>

                <div className="text-muted-foreground space-y-2 text-sm">
                  {email && (
                    <div className="flex items-center justify-center gap-2 md:justify-start">
                      <Mail className="h-4 w-4" />
                      <span>{email}</span>
                    </div>
                  )}
                  {location && (
                    <div className="flex items-center justify-center gap-2 md:justify-start">
                      <MapPin className="h-4 w-4" />
                      <span>{location}</span>
                    </div>
                  )}
                  {date && (
                    <div className="flex items-center justify-center gap-2 md:justify-start">
                      <Calendar className="h-4 w-4" />
                      <span>{date}</span>
                    </div>
                  )}
                </div>

                <div className="border-t border-white/20 pt-4">
                  {tech && tech.length > 0 && (
                    <p className="text-foreground/80 mb-1 text-sm font-semibold">
                      Tech: {tech.join(' ').toUpperCase()}
                    </p>
                  )}
                  {cloud && cloud.length > 0 && (
                    <p className="text-foreground/80 text-sm font-semibold">
                      CLOUD: {cloud.join(', ').toUpperCase()}
                    </p>
                  )}
                </div>
              </div>
            </div>
          ) : variant === 'compact' ? (
            <div className="flex items-center gap-4">
              {avatar && (
                <div className="ring-primary/20 dark:ring-primary/30 relative h-16 w-16 flex-shrink-0 overflow-hidden rounded-xl ring-2">
                  <Image src={avatar} alt={name || 'Profile'} fill className="object-cover" />
                </div>
              )}

              <div className="min-w-0 flex-1">
                {name && (
                  <h3 className="text-foreground mb-1 truncate text-xl font-bold">{name}</h3>
                )}
                {title && <p className="text-muted-foreground mb-2 text-sm">{title}</p>}

                <div className="text-muted-foreground flex flex-wrap gap-4 text-xs">
                  {email && (
                    <div className="flex items-center gap-1">
                      <Mail className="h-3 w-3" />
                      <span className="truncate">{email}</span>
                    </div>
                  )}
                  {location && (
                    <div className="flex items-center gap-1">
                      <MapPin className="h-3 w-3" />
                      <span className="truncate">{location}</span>
                    </div>
                  )}
                </div>

                {(tech.length > 0 || cloud.length > 0) && (
                  <div className="mt-2 flex flex-wrap gap-1">
                    {tech.slice(0, 3).map((item, index) => (
                      <span
                        key={index}
                        className="bg-primary/10 dark:bg-primary/20 text-primary rounded px-2 py-0.5 text-xs font-medium"
                      >
                        {item}
                      </span>
                    ))}
                    {cloud.slice(0, 2).map((item, index) => (
                      <span
                        key={index}
                        className="bg-accent dark:bg-accent/50 text-accent-foreground rounded px-2 py-0.5 text-xs font-medium"
                      >
                        {item}
                      </span>
                    ))}
                  </div>
                )}
              </div>
            </div>
          ) : (
            <div className="flex flex-col gap-6">
              {/* Avatar and Info Section */}
              <div className="flex items-start gap-4">
                {avatar && (
                  <div className="ring-primary/20 dark:ring-primary/30 relative h-20 w-20 flex-shrink-0 overflow-hidden rounded-2xl ring-2">
                    <Image src={avatar} alt={name || 'Profile'} fill className="object-cover" />
                  </div>
                )}

                <div className="min-w-0 flex-1">
                  {name && (
                    <h3 className="text-foreground mb-1 truncate text-2xl font-bold">{name}</h3>
                  )}
                  {title && <p className="text-muted-foreground mb-3 text-sm">{title}</p>}

                  <div className="flex flex-col gap-2">
                    {email && (
                      <div className="text-muted-foreground flex items-center gap-2 text-sm">
                        <Mail className="h-4 w-4 flex-shrink-0" />
                        <span className="truncate">{email}</span>
                      </div>
                    )}
                    {location && (
                      <div className="text-muted-foreground flex items-center gap-2 text-sm">
                        <MapPin className="h-4 w-4 flex-shrink-0" />
                        <span className="truncate">{location}</span>
                      </div>
                    )}
                  </div>
                </div>
              </div>

              {/* Tech Stack */}
              {tech && tech.length > 0 && (
                <div className="space-y-2">
                  <div className="text-foreground flex items-center gap-2 text-sm font-semibold">
                    <Code className="h-4 w-4" />
                    <span>Tech Stack</span>
                  </div>
                  <div className="flex flex-wrap gap-2">
                    {tech.map((item, index) => (
                      <span
                        key={index}
                        className="bg-primary/10 dark:bg-primary/20 text-primary border-primary/20 rounded-full border px-3 py-1 text-xs font-medium"
                      >
                        {item}
                      </span>
                    ))}
                  </div>
                </div>
              )}

              {/* Cloud Services */}
              {cloud && cloud.length > 0 && (
                <div className="space-y-2">
                  <div className="text-foreground flex items-center gap-2 text-sm font-semibold">
                    <Cloud className="h-4 w-4" />
                    <span>Cloud & Tools</span>
                  </div>
                  <div className="flex flex-wrap gap-2">
                    {cloud.map((item, index) => (
                      <span
                        key={index}
                        className="bg-accent dark:bg-accent/50 text-accent-foreground border-border rounded-full border px-3 py-1 text-xs font-medium"
                      >
                        {item}
                      </span>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

Variants

Default Variant

The standard layout with medium-sized avatar and detailed sections.

John Doe

Full Stack Developer

john.doe@example.com
San Francisco, CA
Tech Stack
ReactNext.jsTypeScript
Cloud & Tools
AWSVercelDocker

Elegant Variant

A modern layout with large avatar, gradient text, and clean design. Perfect for hero sections or featured profiles.

Arnab Das

Arnab Das

Design Engineer

arnab.das@example.com
Kolkata, India
March 2025

Tech: NEXT.JS TYPESCRIPT

CLOUD: AWS, GCP


Compact Variant

A space-efficient horizontal layout perfect for grids, sidebars, or list views.

Jane Smith

UI/UX Designer

jane.smith@design.com
New York, NY
FigmaSketchFramerWebflow

Custom Content

You can also pass custom content using the children prop to create any layout you want.

Custom Content

You can pass any children you want to create custom layouts!


All Variants Showcase

Here's a complete showcase of all ProCard variants:

Elegant Variant

Arnab Das

Arnab Das

Design Engineer

arnab.das@example.com
Kolkata, India
March 2025

Tech: NEXT.JS TYPESCRIPT

CLOUD: AWS, GCP

Default Variant

John Doe

Full Stack Developer

john.doe@example.com
San Francisco, CA
Tech Stack
ReactNext.jsTypeScript
Cloud & Tools
AWSVercelDocker

Compact Variant

Jane Smith

UI/UX Designer

jane.smith@design.com
New York, NY
FigmaSketchFramerWebflow

Installation

Manual Installation

Copy the ProCard component to your project:

# Copy the component file
cp components/aesthe-ui/cards/pro-card.tsx your-project/components/

Dependencies

The ProCard component requires these dependencies:

npm install next lucide-react clsx tailwind-merge

Props

ProCardProps

PropTypeDefaultDescription
avatarstring-Avatar image URL
namestring-Person's name
titlestring-Job title or role
emailstring-Email address
locationstring-Location information
datestring-Date information (shown in elegant variant)
techstring[][]Array of technology names
cloudstring[][]Array of cloud service names
variant"default" | "compact" | "elegant""default"Layout variant
childrenReact.ReactNode-Custom content (overrides default layout)
classNamestring-Additional CSS classes

Usage Examples

Basic Profile Card

import { ProCard } from '@/components/aesthe-ui/cards/pro-card'

export default function ProfilePage() {
  return (
    <ProCard
      avatar="https://example.com/avatar.jpg"
      name="John Doe"
      title="Full Stack Developer"
      email="john.doe@example.com"
      location="San Francisco, CA"
      tech={["React", "Next.js", "TypeScript"]}
      cloud={["AWS", "Vercel", "Docker"]}
    />
  )
}

Team Member Grid

import { ProCard } from '@/components/aesthe-ui/cards/pro-card'

const teamMembers = [
  {
    name: "Alice Johnson",
    title: "Frontend Developer",
    email: "alice@company.com",
    location: "Remote",
    tech: ["React", "Vue", "Svelte"],
    cloud: ["Vercel", "Netlify"]
  },
  {
    name: "Bob Smith",
    title: "Backend Developer",
    email: "bob@company.com",
    location: "New York",
    tech: ["Node.js", "Python", "Go"],
    cloud: ["AWS", "Docker", "Kubernetes"]
  }
]

export default function TeamPage() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
      {teamMembers.map((member, index) => (
        <ProCard
          key={index}
          variant="compact"
          name={member.name}
          title={member.title}
          email={member.email}
          location={member.location}
          tech={member.tech}
          cloud={member.cloud}
        />
      ))}
    </div>
  )
}

Hero Section

import { ProCard } from '@/components/aesthe-ui/cards/pro-card'

export default function HeroSection() {
  return (
    <section className="min-h-screen flex items-center justify-center">
      <ProCard
        variant="elegant"
        avatar="https://example.com/founder.jpg"
        name="Sarah Wilson"
        title="Founder & CEO"
        email="sarah@company.com"
        location="San Francisco, CA"
        date="Founded 2020"
        tech={["Next.js", "TypeScript", "Tailwind CSS"]}
        cloud={["AWS", "Vercel", "Stripe"]}
        className="max-w-2xl"
      />
    </section>
  )
}

Customization

Styling

You can customize the ProCard appearance using the className prop:

<ProCard
  className="max-w-lg mx-auto shadow-2xl"
  // ... other props
/>

CSS Variables

The component uses CSS variables for theming. You can override these in your global CSS:

:root {
  --glare-light: 255 255 255;
  --glare-opacity: 0.4;
}

.dark {
  --glare-light: 200 200 255;
  --glare-opacity: 0.25;
}

Custom Animations

The ProCard includes several CSS animations that you can customize:

@keyframes shimmer-sweep {
  0% {
    transform: translateX(-100%) translateY(-100%) rotate(30deg);
  }
  100% {
    transform: translateX(100%) translateY(100%) rotate(30deg);
  }
}

.shimmer-effect {
  animation: shimmer-sweep 0.8s ease-out;
}

Accessibility

The ProCard component includes several accessibility features:

  • Keyboard Navigation - All interactive elements are keyboard accessible
  • Screen Reader Support - Proper ARIA labels and semantic HTML
  • Focus Management - Clear focus indicators for keyboard users
  • Reduced Motion - Respects user's motion preferences

Browser Support

The ProCard component works in all modern browsers that support:

  • CSS Grid and Flexbox
  • CSS Custom Properties (CSS Variables)
  • CSS Transforms and Transitions
  • ES6+ JavaScript features

Performance

The ProCard is optimized for performance:

  • Next.js Image Optimization - Automatic image optimization for avatars
  • Efficient Animations - Uses CSS transforms for smooth 60fps animations
  • Minimal Re-renders - Optimized React state management
  • Lazy Loading - Images load only when needed