Merge version_2 into main #7

Merged
bender merged 13 commits from version_2 into main 2026-02-27 12:57:13 +00:00
13 changed files with 795 additions and 774 deletions

View File

@@ -16,8 +16,7 @@ export const metadata: Metadata = {
openGraph: {
title: "Computer Club - Tech Community & Innovation Hub", description: "Join a vibrant community of tech enthusiasts. Access coding workshops, hackathons, networking events, and mentorship programs.", type: "website", siteName: "Computer Club", images: [
{
url: "http://img.b2bpic.net/free-photo/gradient-illuminated-neon-gaming-desk-setup-with-keyboard_23-2149529414.jpg", alt: "Computer Club Community"
},
url: "http://img.b2bpic.net/free-photo/gradient-illuminated-neon-gaming-desk-setup-with-keyboard_23-2149529414.jpg", alt: "Computer Club Community"},
],
},
twitter: {
@@ -33,9 +32,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<ServiceWrapper>
<body
className={`${karla.variable} antialiased`}
>
<body className={`${karla.variable} antialiased`}>
<Tag />
{children}

View File

@@ -7,12 +7,53 @@ import MetricSplitMediaAbout from '@/components/sections/about/MetricSplitMediaA
import FeatureBorderGlow from '@/components/sections/feature/featureBorderGlow/FeatureBorderGlow';
import ProductCardFour from '@/components/sections/product/ProductCardFour';
import TestimonialCardTwelve from '@/components/sections/testimonial/TestimonialCardTwelve';
import TeamCardFiveFlip from '@/components/sections/team/TeamCardFiveFlip';
import TeamCardFive from '@/components/sections/team/TeamCardFive';
import FaqBase from '@/components/sections/faq/FaqBase';
import FooterLogoEmphasis from '@/components/sections/footer/FooterLogoEmphasis';
import { BookOpen, Calendar, Code, Info, Lightbulb, Rocket, Star, Trophy, Users, Zap, Github, Linkedin, Twitter } from "lucide-react";
import { BookOpen, Calendar, Code, Info, Lightbulb, Rocket, Star, Trophy, Users, Zap } from "lucide-react";
import React, { useState } from "react";
export default function ComputerClubPage() {
const [flippedCards, setFlippedCards] = useState<{ [key: string]: boolean }>({});
const toggleFlip = (memberId: string) => {
setFlippedCards((prev) => ({
...prev,
[memberId]: !prev[memberId],
}));
};
const teamMembers = [
{
id: "1", name: "Alex Johnson", role: "Founder & President", imageSrc: "http://img.b2bpic.net/free-photo/black-businessman-happy-expression_1194-2533.jpg", imageAlt: "Alex Johnson", social: [
{ platform: "linkedin", url: "https://linkedin.com/in/alexjohnson" },
{ platform: "twitter", url: "https://twitter.com/alexjohnson" },
{ platform: "github", url: "https://github.com/alexjohnson" },
],
},
{
id: "2", name: "Jessica Lee", role: "Vice President", imageSrc: "http://img.b2bpic.net/free-photo/smiling-beautiful-middle-aged-business-woman_1262-3085.jpg?_wi=1", imageAlt: "Jessica Lee", social: [
{ platform: "linkedin", url: "https://linkedin.com/in/jessicalee" },
{ platform: "twitter", url: "https://twitter.com/jessicalee" },
{ platform: "github", url: "https://github.com/jessicalee" },
],
},
{
id: "3", name: "David Kumar", role: "Technical Director", imageSrc: "http://img.b2bpic.net/free-photo/smiling-beautiful-middle-aged-business-woman_1262-3085.jpg?_wi=2", imageAlt: "David Kumar", social: [
{ platform: "linkedin", url: "https://linkedin.com/in/davidkumar" },
{ platform: "twitter", url: "https://twitter.com/davidkumar" },
{ platform: "github", url: "https://github.com/davidkumar" },
],
},
{
id: "4", name: "Rachel Martinez", role: "Event Coordinator", imageSrc: "http://img.b2bpic.net/free-photo/smiling-beautiful-middle-aged-business-woman_1262-3085.jpg?_wi=3", imageAlt: "Rachel Martinez", social: [
{ platform: "linkedin", url: "https://linkedin.com/in/rachelmartinez" },
{ platform: "twitter", url: "https://twitter.com/rachelmartinez" },
{ platform: "github", url: "https://github.com/rachelmartinez" },
],
},
];
return (
<ThemeProvider
defaultButtonVariant="shift-hover"
@@ -161,45 +202,83 @@ export default function ComputerClubPage() {
</div>
<div id="team" data-section="team">
<TeamCardFiveFlip
title="Meet Our Leadership"
description="Dedicated professionals driving innovation and community growth"
tag="Leadership"
tagIcon={Users}
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
team={[
{
id: "1", name: "Alex Johnson", role: "Founder & President", imageSrc: "http://img.b2bpic.net/free-photo/black-businessman-happy-expression_1194-2533.jpg", imageAlt: "Alex Johnson", socialLinks: [
{ icon: Github, url: "https://github.com" },
{ icon: Linkedin, url: "https://linkedin.com" },
{ icon: Twitter, url: "https://twitter.com" }
]
},
{
id: "2", name: "Jessica Lee", role: "Vice President", imageSrc: "http://img.b2bpic.net/free-photo/smiling-beautiful-middle-aged-business-woman_1262-3085.jpg?_wi=1", imageAlt: "Jessica Lee", socialLinks: [
{ icon: Github, url: "https://github.com" },
{ icon: Linkedin, url: "https://linkedin.com" },
{ icon: Twitter, url: "https://twitter.com" }
]
},
{
id: "3", name: "David Kumar", role: "Technical Director", imageSrc: "http://img.b2bpic.net/free-photo/smiling-beautiful-middle-aged-business-woman_1262-3085.jpg?_wi=2", imageAlt: "David Kumar", socialLinks: [
{ icon: Github, url: "https://github.com" },
{ icon: Linkedin, url: "https://linkedin.com" },
{ icon: Twitter, url: "https://twitter.com" }
]
},
{
id: "4", name: "Rachel Martinez", role: "Event Coordinator", imageSrc: "http://img.b2bpic.net/free-photo/smiling-beautiful-middle-aged-business-woman_1262-3085.jpg?_wi=3", imageAlt: "Rachel Martinez", socialLinks: [
{ icon: Github, url: "https://github.com" },
{ icon: Linkedin, url: "https://linkedin.com" },
{ icon: Twitter, url: "https://twitter.com" }
]
}
]}
/>
<div className="py-20 md:py-28">
<div className="w-content-width mx-auto">
<div className="mb-16 md:mb-20">
<div className="mb-6">
<span className="inline-flex items-center gap-2 px-4 py-2 rounded-theme-capped bg-primary-cta/10 text-primary-cta text-sm font-medium">
<Users className="w-4 h-4" />
Leadership
</span>
</div>
<h2 className="text-5xl md:text-7xl font-bold mb-6 text-foreground">Meet Our Leadership</h2>
<p className="text-lg md:text-xl text-foreground/80">Dedicated professionals driving innovation and community growth</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8">
{teamMembers.map((member) => {
const isFlipped = flippedCards[member.id];
return (
<div
key={member.id}
className="h-96 cursor-pointer perspective"
onClick={() => toggleFlip(member.id)}
>
<div
className="relative w-full h-full transition-transform duration-500 transform-gpu"
style={{
transformStyle: 'preserve-3d',
transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)',
}}
>
{/* Front of card */}
<div
className="absolute w-full h-full rounded-theme-capped overflow-hidden"
style={{
backfaceVisibility: 'hidden',
}}
>
<img
src={member.imageSrc}
alt={member.imageAlt}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent flex flex-col justify-end p-6">
<h3 className="text-2xl font-bold text-white mb-1">{member.name}</h3>
<p className="text-sm text-white/90">{member.role}</p>
</div>
</div>
{/* Back of card */}
<div
className="absolute w-full h-full rounded-theme-capped overflow-hidden bg-gradient-to-br from-primary-cta to-primary-cta/80 flex flex-col items-center justify-center p-6"
style={{
backfaceVisibility: 'hidden',
transform: 'rotateY(180deg)',
}}
>
<h3 className="text-white font-bold mb-6 text-center">{member.name}</h3>
<div className="flex flex-col gap-4 w-full">
{member.social.map((link) => (
<a
key={link.platform}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="w-full py-3 px-4 rounded-lg bg-white/20 hover:bg-white/30 text-white text-sm font-medium text-center transition-colors duration-200"
>
{link.platform.charAt(0).toUpperCase() + link.platform.slice(1)}
</a>
))}
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
<div id="faq" data-section="faq">

View File

@@ -8,7 +8,7 @@ interface ServiceWrapperProps {
export function ServiceWrapper({ children }: ServiceWrapperProps) {
return (
<div className="min-h-screen w-full bg-white">
<div className="w-full min-h-screen bg-white">
{children}
</div>
);

View File

@@ -38,7 +38,7 @@ export default function NavbarStyleApple({
</div>
{/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-8">
<div className="hidden md:flex space-x-8">
{navItems.map((item) => (
<button
key={item.id}
@@ -54,7 +54,7 @@ export default function NavbarStyleApple({
<div className="md:hidden">
<button
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center justify-center p-2 rounded-md text-gray-700 hover:text-blue-600 hover:bg-gray-100 focus:outline-none transition-colors duration-200"
className="inline-flex items-center justify-center p-2 rounded-md text-gray-700 hover:text-blue-600 focus:outline-none"
>
<svg
className="h-6 w-6"
@@ -75,16 +75,18 @@ export default function NavbarStyleApple({
{/* Mobile Navigation */}
{isOpen && (
<div className="md:hidden pb-4 space-y-2">
{navItems.map((item) => (
<button
key={item.id}
onClick={() => handleNavClick(item.id)}
className="block w-full text-left px-4 py-2 text-gray-700 hover:text-blue-600 hover:bg-gray-100 rounded-md font-medium transition-colors duration-200"
>
{item.name}
</button>
))}
<div className="md:hidden pb-4">
<div className="space-y-2">
{navItems.map((item) => (
<button
key={item.id}
onClick={() => handleNavClick(item.id)}
className="block w-full text-left px-3 py-2 rounded-md text-gray-700 hover:bg-blue-50 hover:text-blue-600 font-medium transition-colors duration-200"
>
{item.name}
</button>
))}
</div>
</div>
)}
</div>

View File

@@ -1,6 +1,7 @@
"use client";
import React from "react";
import Image from "next/image";
import { LucideIcon } from "lucide-react";
interface Metric {
@@ -8,7 +9,7 @@ interface Metric {
title: string;
}
interface Props {
interface MetricSplitMediaAboutProps {
tag: string;
tagIcon: LucideIcon;
title: string;
@@ -32,7 +33,7 @@ export default function MetricSplitMediaAbout({
useInvertedBackground = false,
mediaAnimation = "slide-up",
metricsAnimation = "slide-up",
}: Props) {
}: MetricSplitMediaAboutProps) {
const bgClass = useInvertedBackground ? "bg-slate-900" : "bg-white";
const textClass = useInvertedBackground ? "text-white" : "text-slate-900";
const descriptionClass = useInvertedBackground
@@ -42,20 +43,47 @@ export default function MetricSplitMediaAbout({
const getAnimationClass = (animation: string) => {
switch (animation) {
case "slide-up":
return "animate-in fade-in slide-in-from-bottom-4 duration-700";
case "fade":
return "animate-in fade-in duration-700";
return "animate-slide-up";
case "fade-in":
return "animate-fade-in";
default:
return "";
}
};
return (
<section className={`${bgClass} py-16 md:py-24 transition-colors duration-300`}>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<section className={`${bgClass} py-16 md:py-24 lg:py-32 transition-colors duration-300`}>
<style>{`
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.animate-slide-up {
animation: slideUp 0.6s ease-out forwards;
}
.animate-fade-in {
animation: fadeIn 0.6s ease-out forwards;
}
`}</style>
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{/* Left Content */}
<div className={`${getAnimationClass(mediaAnimation)}`}>
<div className={`${getAnimationClass(metricsAnimation)}`}>
{/* Tag */}
<div className="flex items-center gap-2 mb-6">
<TagIcon className="w-5 h-5 text-blue-600" />
@@ -65,29 +93,23 @@ export default function MetricSplitMediaAbout({
</div>
{/* Title */}
<h2
className={`text-4xl md:text-5xl font-bold ${textClass} mb-6 leading-tight`}
>
<h2 className={`${textClass} text-3xl md:text-4xl lg:text-5xl font-bold mb-6 leading-tight`}>
{title}
</h2>
{/* Description */}
<p className={`${descriptionClass} text-lg leading-relaxed mb-8`}>
<p className={`${descriptionClass} text-base md:text-lg mb-10 leading-relaxed`}>
{description}
</p>
{/* Metrics */}
<div
className={`grid grid-cols-2 gap-6 ${getAnimationClass(metricsAnimation)}`}
>
<div className="grid grid-cols-2 gap-8">
{metrics.map((metric, index) => (
<div key={index} className="flex flex-col">
<span
className={`text-3xl md:text-4xl font-bold ${textClass} mb-2`}
>
<span className={`${textClass} text-3xl md:text-4xl font-bold mb-2`}>
{metric.value}
</span>
<span className={`${descriptionClass} text-sm font-medium`}>
<span className={`${descriptionClass} text-sm md:text-base font-medium`}>
{metric.title}
</span>
</div>
@@ -95,15 +117,16 @@ export default function MetricSplitMediaAbout({
</div>
</div>
{/* Right Image */}
{/* Right Media */}
<div className={`${getAnimationClass(mediaAnimation)}`}>
<div className="relative overflow-hidden rounded-2xl shadow-2xl">
<img
<div className="relative w-full aspect-square rounded-lg overflow-hidden shadow-xl">
<Image
src={imageSrc}
alt={imageAlt}
className="w-full h-auto object-cover aspect-square md:aspect-auto"
fill
className="object-cover"
priority
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" />
</div>
</div>
</div>

View File

@@ -12,7 +12,7 @@ interface FaqBaseProps {
title: string;
description: string;
tag: string;
textboxLayout: "default" | "compact" | "wide";
textboxLayout: "default" | "centered" | "wide";
useInvertedBackground: boolean;
faqsAnimation: "slide-up" | "fade-in" | "bounce";
faqs: FaqItem[];
@@ -29,40 +29,39 @@ export default function FaqBase({
}: FaqBaseProps) {
const [expandedId, setExpandedId] = useState<string | null>(null);
const toggleExpanded = (id: string) => {
const toggleFaq = (id: string) => {
setExpandedId(expandedId === id ? null : id);
};
const getAnimationClass = () => {
switch (faqsAnimation) {
case "fade-in":
return "animate-fade-in";
case "bounce":
return "animate-bounce";
case "slide-up":
default:
return "animate-slide-up";
}
};
const getTextboxLayoutClass = () => {
switch (textboxLayout) {
case "compact":
return "max-w-2xl";
case "centered":
return "text-center max-w-2xl mx-auto";
case "wide":
return "max-w-6xl";
case "default":
return "max-w-4xl mx-auto";
default:
return "max-w-4xl";
return "max-w-3xl";
}
};
const getAnimationClass = (index: number) => {
switch (faqsAnimation) {
case "fade-in":
return `opacity-0 animate-fadeIn`;
case "bounce":
return `opacity-0 animate-bounce`;
case "slide-up":
default:
return `opacity-0 animate-slideUp`;
}
};
const bgClass = useInvertedBackground
? "bg-gray-900 text-white"
: "bg-white text-gray-900";
? "bg-slate-900 text-white"
: "bg-white text-slate-900";
return (
<section className={`py-16 px-4 ${bgClass}`}>
<section className={`py-16 px-4 sm:px-6 lg:px-8 ${bgClass}`}>
<style>{`
@keyframes slideUp {
from {
@@ -82,90 +81,76 @@ export default function FaqBase({
opacity: 1;
}
}
.animate-slide-up {
animation: slideUp 0.6s ease-out;
.animate-slideUp {
animation: slideUp 0.6s ease-out forwards;
}
.animate-fade-in {
animation: fadeIn 0.6s ease-out;
.animate-fadeIn {
animation: fadeIn 0.6s ease-out forwards;
}
`}</style>
<div className={`mx-auto ${getTextboxLayoutClass()}`}>
{/* Header Section */}
<div className="mb-12 text-center">
{tag && (
<div className="mb-4 inline-block">
<span className="inline-block bg-blue-100 text-blue-800 px-4 py-1 rounded-full text-sm font-semibold">
{tag}
</span>
</div>
)}
<h2 className="text-4xl font-bold mb-4">{title}</h2>
<p className={`text-lg ${useInvertedBackground ? "text-gray-300" : "text-gray-600"}`}>
{description}
</p>
<div className={`${getTextboxLayoutClass()} mb-12`}>
<div className="mb-4">
<span className="inline-block px-3 py-1 text-sm font-semibold text-blue-600 bg-blue-100 rounded-full">
{tag}
</span>
</div>
<h2 className="text-4xl font-bold mb-4">{title}</h2>
<p className="text-lg opacity-75">{description}</p>
</div>
{/* FAQ Items */}
<div className="space-y-4">
{faqs.map((faq, index) => (
<div
key={faq.id}
className={`${getAnimationClass()} border rounded-lg overflow-hidden transition-all duration-300 ${
<div className="max-w-3xl mx-auto space-y-4">
{faqs.map((faq, index) => (
<div
key={faq.id}
className={`${getAnimationClass(index)} border rounded-lg overflow-hidden transition-all duration-300 ${
useInvertedBackground
? "border-slate-700 hover:border-blue-500"
: "border-slate-200 hover:border-blue-400"
}`}
style={{
animationDelay: `${index * 0.1}s`,
}}
>
<button
onClick={() => toggleFaq(faq.id)}
className={`w-full px-6 py-4 flex items-center justify-between font-semibold transition-colors duration-200 ${
useInvertedBackground
? "border-gray-700 hover:border-blue-500"
: "border-gray-200 hover:border-blue-500"
? "hover:bg-slate-800"
: "hover:bg-slate-50"
}`}
style={{
animationDelay: `${index * 0.1}s`,
}}
>
<button
onClick={() => toggleExpanded(faq.id)}
className={`w-full px-6 py-4 flex items-center justify-between font-semibold transition-colors ${
<span className="text-left">{faq.title}</span>
<svg
className={`w-5 h-5 transition-transform duration-300 ${
expandedId === faq.id ? "rotate-180" : ""
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</button>
{expandedId === faq.id && (
<div
className={`px-6 py-4 border-t ${
useInvertedBackground
? "bg-gray-800 hover:bg-gray-700"
: "bg-gray-50 hover:bg-gray-100"
? "border-slate-700 bg-slate-800"
: "border-slate-200 bg-slate-50"
}`}
>
<span className="text-left">{faq.title}</span>
<svg
className={`w-5 h-5 transition-transform duration-300 ${
expandedId === faq.id ? "rotate-180" : ""
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
</button>
{expandedId === faq.id && (
<div
className={`px-6 py-4 border-t ${
useInvertedBackground
? "border-gray-700 bg-gray-900"
: "border-gray-200 bg-gray-50"
}`}
>
<p
className={`${
useInvertedBackground ? "text-gray-300" : "text-gray-700"
}`}
>
{faq.content}
</p>
</div>
)}
</div>
))}
</div>
<p className="opacity-90">{faq.content}</p>
</div>
)}
</div>
))}
</div>
</section>
);

View File

@@ -51,88 +51,71 @@ export default function FeatureBorderGlow({
key={index}
className="group relative h-80 rounded-xl overflow-hidden bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 border border-gray-200 dark:border-gray-700 transition-all duration-300 hover:shadow-xl"
>
{/* Border Glow Effect */}
{/* Glow Border Effect */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 via-purple-500/20 to-pink-500/20 rounded-xl blur-xl" />
</div>
{/* Front Content */}
<div className="absolute inset-0 p-6 flex flex-col justify-center items-center text-center z-10 group-hover:opacity-0 transition-opacity duration-300">
<div className="mb-4 p-3 rounded-lg bg-blue-500/10 dark:bg-blue-500/20">
<Icon className="w-8 h-8 text-blue-600 dark:text-blue-400" />
{/* Front Side */}
<div className="absolute inset-0 p-6 flex flex-col items-start justify-start group-hover:opacity-0 transition-opacity duration-300 z-10">
<div className="w-12 h-12 rounded-lg bg-blue-500/10 flex items-center justify-center mb-4 group-hover:scale-110 transition-transform duration-300">
<Icon className="w-6 h-6 text-blue-600 dark:text-blue-400" />
</div>
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-3">
{feature.title}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
<p className="text-gray-600 dark:text-gray-300 text-sm leading-relaxed">
{feature.description}
</p>
</div>
{/* Back Content (Hover) */}
<div className="absolute inset-0 p-6 flex flex-col justify-center items-center text-center z-10 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className="mb-6">
<h3 className="text-xl font-bold text-gray-900 dark:text-white mb-4">
Connect
</h3>
</div>
<div className="flex gap-4 justify-center">
<a
href="#"
className="p-3 rounded-full bg-blue-500 hover:bg-blue-600 text-white transition-colors duration-200"
aria-label="LinkedIn"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
{/* Back Side */}
<div className="absolute inset-0 p-6 flex flex-col items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-20 bg-gradient-to-br from-blue-600 to-purple-600">
<div className="text-center">
<h4 className="text-white font-bold text-lg mb-4">
{feature.title}
</h4>
<div className="flex gap-3 justify-center">
<a
href="#"
className="w-10 h-10 rounded-full bg-white/20 hover:bg-white/30 flex items-center justify-center text-white transition-colors duration-200"
aria-label="LinkedIn"
>
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
</svg>
</a>
<a
href="#"
className="p-3 rounded-full bg-blue-400 hover:bg-blue-500 text-white transition-colors duration-200"
aria-label="Twitter"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
</svg>
</a>
<a
href="#"
className="w-10 h-10 rounded-full bg-white/20 hover:bg-white/30 flex items-center justify-center text-white transition-colors duration-200"
aria-label="Twitter"
>
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2s9 5 20 5a9.5 9.5 0 00-9-5.5c4.75 2.25 9-1 9-5.5a4.5 4.5 0 00-.08-.83A7.72 7.72 0 0023 3z" />
</svg>
</a>
<a
href="#"
className="p-3 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white transition-colors duration-200"
aria-label="Instagram"
>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2s9 5 20 5a9.5 9.5 0 00-9-5.5c4.75 2.25 7-7 7-7a10.6 10.6 0 01-9.5 5" />
</svg>
</a>
<a
href="#"
className="w-10 h-10 rounded-full bg-white/20 hover:bg-white/30 flex items-center justify-center text-white transition-colors duration-200"
aria-label="GitHub"
>
<rect
x="2"
y="2"
width="20"
height="20"
rx="5"
ry="5"
fill="none"
stroke="currentColor"
strokeWidth="2"
/>
<path
d="M16 11.37A4 4 0 1112.63 8 4 4 0 0116 11.37z"
fill="none"
stroke="currentColor"
strokeWidth="2"
/>
<circle cx="17.5" cy="6.5" r="1.5" fill="currentColor" />
</svg>
</a>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v 3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
</svg>
</a>
</div>
</div>
</div>
</div>

View File

@@ -25,23 +25,28 @@ export default function FooterLogoEmphasis({
<div className="max-w-6xl mx-auto">
{/* Logo Section */}
<div className="mb-12 pb-8 border-b border-slate-700">
<h2 className="text-3xl font-bold bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
{logoText}
</h2>
<p className="text-slate-400 mt-2">
Building the future of technology together
<div className="flex items-center gap-3 mb-2">
<div className="w-10 h-10 bg-gradient-to-br from-blue-400 to-purple-600 rounded-lg flex items-center justify-center font-bold text-lg">
C
</div>
<h2 className="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
{logoText}
</h2>
</div>
<p className="text-slate-400 text-sm">
Building amazing digital experiences together
</p>
</div>
{/* Links Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mb-8">
{columns.map((column, columnIndex) => (
<div key={columnIndex} className="space-y-4">
{column.items.map((item, itemIndex) => (
<Link
key={itemIndex}
href={item.href}
className="block text-slate-300 hover:text-blue-400 transition-colors duration-200 font-medium"
className="block text-slate-300 hover:text-blue-400 transition-colors duration-200 text-sm font-medium"
>
{item.label}
</Link>
@@ -51,22 +56,36 @@ export default function FooterLogoEmphasis({
</div>
{/* Bottom Section */}
<div className="border-t border-slate-700 pt-8 flex flex-col md:flex-row justify-between items-center">
<div className="border-t border-slate-700 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
<p className="text-slate-400 text-sm">
© 2024 {logoText}. All rights reserved.
</p>
<div className="flex gap-6 mt-4 md:mt-0">
<div className="flex gap-6">
<Link
href="#"
className="text-slate-400 hover:text-blue-400 transition-colors"
className="text-slate-400 hover:text-blue-400 transition-colors duration-200"
>
Privacy
<span className="sr-only">Twitter</span>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M8.29 20c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-7.007 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
</svg>
</Link>
<Link
href="#"
className="text-slate-400 hover:text-blue-400 transition-colors"
className="text-slate-400 hover:text-blue-400 transition-colors duration-200"
>
Terms
<span className="sr-only">Discord</span>
<svg
className="w-5 h-5"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.211.375-.444.864-.607 1.25a18.27 18.27 0 00-5.487 0c-.163-.386-.395-.875-.607-1.25a.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.042-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.294.075.075 0 01.078-.01c3.928 1.793 8.18 1.793 12.062 0a.075.075 0 01.079.009c.12.098.246.198.373.295a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.076.076 0 00-.041.107c.352.699.764 1.364 1.225 1.994a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.057c.5-4.761-.838-8.898-3.549-12.562a.06.06 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-.965-2.157-2.156 0-1.193.964-2.157 2.157-2.157 1.193 0 2.156.964 2.157 2.157 0 1.19-.964 2.156-2.157 2.156zm7.975 0c-1.183 0-2.157-.965-2.157-2.156 0-1.193.964-2.157 2.157-2.157 1.192 0 2.156.964 2.157 2.157 0 1.19-.965 2.156-2.157 2.156z" />
</svg>
</Link>
</div>
</div>

View File

@@ -9,7 +9,7 @@ interface Button {
href: string;
}
interface Background {
interface BackgroundConfig {
variant: "animated-grid" | "solid" | "gradient";
}
@@ -18,12 +18,12 @@ interface HeroBillboardScrollProps {
description: string;
tag: string;
tagIcon: LucideIcon;
tagAnimation?: string;
background?: Background;
tagAnimation: string;
background: BackgroundConfig;
imageSrc: string;
imageAlt: string;
buttons: Button[];
buttonAnimation?: string;
buttonAnimation: string;
}
export default function HeroBillboardScroll({
@@ -31,28 +31,33 @@ export default function HeroBillboardScroll({
description,
tag,
tagIcon: TagIcon,
tagAnimation = "slide-up",
background = { variant: "animated-grid" },
tagAnimation,
background,
imageSrc,
imageAlt,
buttons,
buttonAnimation = "slide-up",
buttonAnimation,
}: HeroBillboardScrollProps) {
const getBackgroundClass = () => {
switch (background.variant) {
case "animated-grid":
return "bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900";
case "solid":
return "bg-slate-900";
case "gradient":
return "bg-gradient-to-r from-blue-600 to-purple-600";
case "solid":
return "bg-slate-900";
default:
return "bg-slate-900";
}
};
const getAnimationDelay = (index: number) => {
return `${index * 100}ms`;
const getAnimationClass = (animation: string) => {
switch (animation) {
case "slide-up":
return "animate-in fade-in slide-in-from-bottom-4 duration-700";
default:
return "animate-in fade-in duration-700";
}
};
return (
@@ -64,50 +69,43 @@ export default function HeroBillboardScroll({
</div>
)}
{/* Gradient Orbs */}
<div className="absolute top-0 left-1/4 w-96 h-96 bg-blue-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-pulse" />
<div className="absolute bottom-0 right-1/4 w-96 h-96 bg-purple-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20 animate-pulse" />
{/* Gradient Overlay */}
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-transparent to-slate-900/50" />
<div className="relative z-10 flex items-center justify-between min-h-screen px-6 md:px-12 lg:px-20">
<div className="relative z-10 flex items-center justify-between min-h-screen px-6 sm:px-8 lg:px-12">
{/* Left Content */}
<div className="flex-1 max-w-2xl">
{/* Tag */}
<div
className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-blue-500/10 border border-blue-500/30 mb-6 animate-fade-in"
style={{
animation: `fadeInUp 0.6s ease-out ${getAnimationDelay(0)}`,
}}
className={`inline-flex items-center gap-2 mb-6 px-4 py-2 rounded-full bg-slate-800/50 border border-slate-700 ${getAnimationClass(
tagAnimation
)}`}
>
<TagIcon className="w-4 h-4 text-blue-400" />
<span className="text-sm font-medium text-blue-300">{tag}</span>
<span className="text-sm font-medium text-slate-300">{tag}</span>
</div>
{/* Title */}
<h1
className="text-5xl md:text-6xl lg:text-7xl font-bold text-white mb-6 leading-tight animate-fade-in"
style={{
animation: `fadeInUp 0.6s ease-out ${getAnimationDelay(1)}`,
}}
className={`text-5xl sm:text-6xl lg:text-7xl font-bold text-white mb-6 leading-tight ${getAnimationClass(
tagAnimation
)}`}
>
{title}
</h1>
{/* Description */}
<p
className="text-lg md:text-xl text-gray-300 mb-8 leading-relaxed max-w-xl animate-fade-in"
style={{
animation: `fadeInUp 0.6s ease-out ${getAnimationDelay(2)}`,
}}
className={`text-lg sm:text-xl text-slate-300 mb-8 leading-relaxed max-w-xl ${getAnimationClass(
tagAnimation
)}`}
>
{description}
</p>
{/* Buttons */}
<div
className="flex flex-wrap gap-4 animate-fade-in"
style={{
animation: `fadeInUp 0.6s ease-out ${getAnimationDelay(3)}`,
}}
className={`flex flex-wrap gap-4 ${getAnimationClass(buttonAnimation)}`}
>
{buttons.map((button, index) => (
<Link
@@ -115,8 +113,8 @@ export default function HeroBillboardScroll({
href={button.href}
className={`px-8 py-3 rounded-lg font-semibold transition-all duration-300 ${
index === 0
? "bg-blue-600 text-white hover:bg-blue-700 shadow-lg hover:shadow-blue-500/50"
: "bg-transparent text-white border-2 border-white hover:bg-white hover:text-slate-900"
? "bg-blue-600 text-white hover:bg-blue-700 shadow-lg hover:shadow-blue-600/50"
: "bg-slate-800 text-white border border-slate-700 hover:bg-slate-700 hover:border-slate-600"
}`}
>
{button.text}
@@ -126,12 +124,7 @@ export default function HeroBillboardScroll({
</div>
{/* Right Image */}
<div
className="hidden lg:flex flex-1 justify-end animate-fade-in"
style={{
animation: `fadeInUp 0.6s ease-out ${getAnimationDelay(4)}`,
}}
>
<div className="hidden lg:flex flex-1 items-center justify-center">
<div className="relative w-full max-w-md h-96 rounded-2xl overflow-hidden shadow-2xl">
<img
src={imageSrc}
@@ -145,40 +138,13 @@ export default function HeroBillboardScroll({
{/* Scroll Indicator */}
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-10">
<div className="flex flex-col items-center gap-2 animate-bounce">
<span className="text-sm text-gray-400">Scroll to explore</span>
<svg
className="w-6 h-6 text-gray-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 14l-7 7m0 0l-7-7m7 7V3"
/>
</svg>
<div className="flex flex-col items-center gap-2">
<span className="text-sm text-slate-400">Scroll to explore</span>
<div className="w-6 h-10 border-2 border-slate-600 rounded-full flex items-start justify-center p-2">
<div className="w-1 h-2 bg-slate-400 rounded-full animate-bounce" />
</div>
</div>
</div>
<style jsx>{`
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
opacity: 0;
}
`}</style>
</section>
);
}

View File

@@ -61,212 +61,169 @@ export default function ProductCardFour({
const getAnimationClass = () => {
switch (animationType) {
case "slide-up":
return "animate-slide-up";
return "animate-in slide-in-from-bottom-4 duration-500";
case "fade-in":
return "animate-fade-in";
return "animate-in fade-in duration-500";
case "scale":
return "animate-scale";
return "animate-in zoom-in-50 duration-500";
default:
return "animate-fade-in";
return "animate-in fade-in duration-500";
}
};
const bgClass = useInvertedBackground ? "bg-gray-900" : "bg-white";
const bgClass = useInvertedBackground ? "bg-slate-900" : "bg-white";
const textColorClass = useInvertedBackground ? "text-white" : "text-slate-900";
return (
<section className={`py-16 px-4 sm:px-6 lg:px-8 ${bgClass}`}>
<section className={`${bgClass} py-16 px-4 md:px-8 lg:px-12`}>
<div className="max-w-7xl mx-auto">
<div className="mb-12">
<div className="flex items-center gap-2 mb-4">
<TagIcon className="w-5 h-5 text-blue-600" />
<TagIcon className="w-5 h-5" />
<span className="text-sm font-semibold text-blue-600 uppercase tracking-wide">
{tag}
</span>
</div>
<h2
className={`text-4xl sm:text-5xl font-bold mb-4 ${
useInvertedBackground ? "text-white" : "text-gray-900"
}`}
>
<h2 className={`text-4xl md:text-5xl font-bold mb-4 ${textColorClass}`}>
{title}
</h2>
<p
className={`text-lg max-w-2xl ${
useInvertedBackground ? "text-gray-300" : "text-gray-600"
}`}
className={`text-lg ${
useInvertedBackground ? "text-slate-300" : "text-slate-600"
} max-w-2xl`}
>
{description}
</p>
</div>
<div className={`grid ${getGridClass()} gap-8`}>
{products.map((product) => (
{products.map((product, index) => (
<div
key={product.id}
className={`h-96 cursor-pointer perspective ${getAnimationClass()}`}
onClick={() => toggleFlip(product.id)}
className={`${getAnimationClass()}`}
style={{
animationDelay: `${index * 100}ms`,
}}
>
<div
className="relative w-full h-full transition-transform duration-500 transform-gpu"
style={{
transformStyle: "preserve-3d",
transform: flipped[product.id]
? "rotateY(180deg)"
: "rotateY(0deg)",
}}
className="h-96 cursor-pointer perspective"
onClick={() => toggleFlip(product.id)}
>
{/* Front of card */}
<div
className="absolute w-full h-full bg-white rounded-lg shadow-lg overflow-hidden"
style={{ backfaceVisibility: "hidden" }}
>
<div className="relative w-full h-64">
<Image
src={product.imageSrc}
alt={product.imageAlt}
fill
className="object-cover"
/>
</div>
<div className="p-6">
<h3 className="text-xl font-bold text-gray-900 mb-2">
{product.name}
</h3>
<p className="text-sm text-gray-600 mb-3">
{product.variant}
</p>
<div className="flex items-center justify-between">
<span className="text-lg font-semibold text-blue-600">
{product.price}
</span>
<span className="text-xs text-gray-500">Click to flip</span>
</div>
</div>
</div>
{/* Back of card */}
<div
className="absolute w-full h-full bg-blue-600 rounded-lg shadow-lg overflow-hidden flex flex-col items-center justify-center p-6"
className="relative w-full h-full transition-transform duration-500 transform-gpu"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
transformStyle: "preserve-3d",
transform: flipped[product.id]
? "rotateY(180deg)"
: "rotateY(0deg)",
}}
>
<h3 className="text-white text-xl font-bold mb-6 text-center">
Connect With Us
</h3>
<div className="flex gap-4 flex-wrap justify-center">
<a
href="#"
className="w-12 h-12 bg-white bg-opacity-20 hover:bg-opacity-30 rounded-full flex items-center justify-center text-white transition-all duration-300"
title="LinkedIn"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
</svg>
</a>
<a
href="#"
className="w-12 h-12 bg-white bg-opacity-20 hover:bg-opacity-30 rounded-full flex items-center justify-center text-white transition-all duration-300"
title="Twitter"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2s9 5 20 5a9.5 9.5 0 00-9-5.5c4.75 2.25 9 0 11-4s1-8.75 0-10c-.5-.75-1-2-3-4z" />
</svg>
</a>
<a
href="#"
className="w-12 h-12 bg-white bg-opacity-20 hover:bg-opacity-30 rounded-full flex items-center justify-center text-white transition-all duration-300"
title="Facebook"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
</svg>
</a>
<a
href="#"
className="w-12 h-12 bg-white bg-opacity-20 hover:bg-opacity-30 rounded-full flex items-center justify-center text-white transition-all duration-300"
title="Email"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" />
</svg>
</a>
{/* Front of card */}
<div
className="absolute w-full h-full bg-white rounded-lg shadow-lg overflow-hidden"
style={{
backfaceVisibility: "hidden",
}}
>
<div className="relative w-full h-64">
<Image
src={product.imageSrc}
alt={product.imageAlt}
fill
className="object-cover"
/>
</div>
<div className="p-6">
<h3 className="text-xl font-bold text-slate-900 mb-2">
{product.name}
</h3>
<p className="text-sm text-slate-600 mb-3">
{product.variant}
</p>
<div className="text-lg font-semibold text-blue-600">
{product.price}
</div>
<p className="text-xs text-slate-500 mt-3">
Hover to see more
</p>
</div>
</div>
{/* Back of card */}
<div
className="absolute w-full h-full bg-gradient-to-br from-blue-600 to-blue-800 rounded-lg shadow-lg p-6 flex flex-col justify-center items-center"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
>
<h3 className="text-white text-xl font-bold mb-6 text-center">
Connect With Us
</h3>
<div className="flex gap-4 justify-center flex-wrap">
<a
href="#"
className="w-12 h-12 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 flex items-center justify-center text-white transition-all duration-300 transform hover:scale-110"
title="LinkedIn"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z" />
</svg>
</a>
<a
href="#"
className="w-12 h-12 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 flex items-center justify-center text-white transition-all duration-300 transform hover:scale-110"
title="Twitter"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M23 3a10.9 10.9 0 01-3.14 1.53 4.48 4.48 0 00-7.86 3v1A10.66 10.66 0 013 4s-4 9 5 13a11.64 11.64 0 01-7 2s9 5 20 5a9.5 9.5 0 00-9-5.5c4.75 2.25 7-7 7-7a10.6 10.6 0 01-9-5.5z" />
</svg>
</a>
<a
href="#"
className="w-12 h-12 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 flex items-center justify-center text-white transition-all duration-300 transform hover:scale-110"
title="Facebook"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
</svg>
</a>
<a
href="#"
className="w-12 h-12 rounded-full bg-white bg-opacity-20 hover:bg-opacity-30 flex items-center justify-center text-white transition-all duration-300 transform hover:scale-110"
title="Email"
>
<svg
className="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" />
</svg>
</a>
</div>
<p className="text-white text-xs mt-6 text-center opacity-75">
Click to flip back
</p>
</div>
<p className="text-white text-sm mt-6 text-center">
Click to flip back
</p>
</div>
</div>
</div>
))}
</div>
</div>
<style jsx>{`
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes scale {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
.animate-slide-up {
animation: slide-up 0.6s ease-out;
}
.animate-fade-in {
animation: fade-in 0.6s ease-out;
}
.animate-scale {
animation: scale 0.6s ease-out;
}
.perspective {
perspective: 1000px;
}
`}</style>
</section>
);
}

View File

@@ -1,148 +1,193 @@
"use client";
import CardStackTextBox from "@/components/cardStack/CardStackTextBox";
import MediaContent from "@/components/shared/MediaContent";
import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation";
import { cls } from "@/lib/utils";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, CardAnimationType, TitleSegment, ButtonAnimationType } from "@/components/cardStack/types";
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
import { useState } from "react";
import { Mail, Linkedin, Twitter, Github } from "lucide-react";
type TeamMember = {
id: string;
interface TeamMember {
id: number;
name: string;
role: string;
imageSrc?: string;
videoSrc?: string;
imageAlt?: string;
videoAriaLabel?: string;
};
interface TeamCardFiveProps {
team: TeamMember[];
animationType: CardAnimationType;
title: string;
titleSegments?: TitleSegment[];
description: string;
textboxLayout: TextboxLayout;
useInvertedBackground: InvertedBackground;
tag?: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
buttons?: ButtonConfig[];
buttonAnimation?: ButtonAnimationType;
ariaLabel?: string;
className?: string;
containerClassName?: string;
textBoxTitleClassName?: string;
textBoxTitleImageWrapperClassName?: string;
textBoxTitleImageClassName?: string;
textBoxDescriptionClassName?: string;
textBoxClassName?: string;
textBoxTagClassName?: string;
textBoxButtonContainerClassName?: string;
textBoxButtonClassName?: string;
textBoxButtonTextClassName?: string;
gridClassName?: string;
cardClassName?: string;
mediaWrapperClassName?: string;
mediaClassName?: string;
nameClassName?: string;
roleClassName?: string;
image: string;
email?: string;
linkedin?: string;
twitter?: string;
github?: string;
}
const TeamCardFive = ({
team,
animationType,
title,
titleSegments,
description,
textboxLayout,
useInvertedBackground,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
ariaLabel = "Team section",
className = "",
containerClassName = "",
textBoxTitleClassName = "",
textBoxTitleImageWrapperClassName = "",
textBoxTitleImageClassName = "",
textBoxDescriptionClassName = "",
textBoxClassName = "",
textBoxTagClassName = "",
textBoxButtonContainerClassName = "",
textBoxButtonClassName = "",
textBoxButtonTextClassName = "",
gridClassName = "",
cardClassName = "",
mediaWrapperClassName = "",
mediaClassName = "",
nameClassName = "",
roleClassName = "",
}: TeamCardFiveProps) => {
const { itemRefs } = useCardAnimation({ animationType, itemCount: team.length });
interface Props {
members?: TeamMember[];
}
const defaultMembers: TeamMember[] = [
{
id: 1,
name: "Sarah Johnson",
role: "Chief Executive Officer",
image: "https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=400&h=400&fit=crop",
email: "sarah@example.com",
linkedin: "https://linkedin.com",
twitter: "https://twitter.com",
github: "https://github.com",
},
{
id: 2,
name: "Michael Chen",
role: "Chief Technology Officer",
image: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=400&h=400&fit=crop",
email: "michael@example.com",
linkedin: "https://linkedin.com",
twitter: "https://twitter.com",
github: "https://github.com",
},
{
id: 3,
name: "Emily Rodriguez",
role: "Chief Financial Officer",
image: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&h=400&fit=crop",
email: "emily@example.com",
linkedin: "https://linkedin.com",
twitter: "https://twitter.com",
github: "https://github.com",
},
{
id: 4,
name: "David Park",
role: "Chief Product Officer",
image: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=400&h=400&fit=crop",
email: "david@example.com",
linkedin: "https://linkedin.com",
twitter: "https://twitter.com",
github: "https://github.com",
},
];
export default function TeamCardFive({ members = defaultMembers }: Props) {
const [flipped, setFlipped] = useState<{ [key: number]: boolean }>({});
const toggleFlip = (id: number) => {
setFlipped((prev) => ({
...prev,
[id]: !prev[id],
}));
};
return (
<section
aria-label={ariaLabel}
className={cls("relative py-20 w-full", useInvertedBackground && "bg-foreground", className)}
>
<div className={cls("w-content-width mx-auto flex flex-col gap-8", containerClassName)}>
<CardStackTextBox
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
textBoxClassName={textBoxClassName}
titleClassName={textBoxTitleClassName}
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
titleImageClassName={textBoxTitleImageClassName}
descriptionClassName={textBoxDescriptionClassName}
tagClassName={textBoxTagClassName}
buttonContainerClassName={textBoxButtonContainerClassName}
buttonClassName={textBoxButtonClassName}
buttonTextClassName={textBoxButtonTextClassName}
/>
<section className="py-16 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-white to-gray-50">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
Meet Our Leadership
</h2>
<p className="text-xl text-gray-600">
Hover over cards to see social links
</p>
</div>
<div className={cls("flex flex-row flex-wrap gap-y-6 md:gap-x-0 justify-center", gridClassName)}>
{team.map((member, index) => (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{members.map((member) => (
<div
key={member.id}
ref={(el) => { itemRefs.current[index] = el; }}
className={cls("relative flex flex-col items-center text-center w-[55%] md:w-[28%] -mx-[4%] md:-mx-[2%]", cardClassName)}
className="h-80 cursor-pointer perspective"
onClick={() => toggleFlip(member.id)}
>
<div className={cls("relative card w-full aspect-square rounded-theme overflow-hidden p-2 mb-4", mediaWrapperClassName)}>
<MediaContent
imageSrc={member.imageSrc}
videoSrc={member.videoSrc}
imageAlt={member.imageAlt || member.name}
videoAriaLabel={member.videoAriaLabel || member.name}
imageClassName={cls("relative z-1 w-full h-full object-cover rounded-theme!", mediaClassName)}
/>
<div
className="relative w-full h-full transition-transform duration-500 transform-gpu"
style={{
transformStyle: "preserve-3d",
transform: flipped[member.id]
? "rotateY(180deg)"
: "rotateY(0deg)",
}}
>
{/* Front of card */}
<div
className="absolute w-full h-full bg-white rounded-lg shadow-lg overflow-hidden"
style={{
backfaceVisibility: "hidden",
}}
>
<div className="relative w-full h-full flex flex-col items-center justify-center">
<img
src={member.image}
alt={member.name}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black via-transparent to-transparent opacity-60"></div>
<div className="absolute bottom-0 left-0 right-0 p-6 text-white">
<h3 className="text-2xl font-bold mb-2">{member.name}</h3>
<p className="text-sm text-gray-200">{member.role}</p>
</div>
</div>
</div>
{/* Back of card */}
<div
className="absolute w-full h-full bg-gradient-to-br from-blue-600 to-blue-800 rounded-lg shadow-lg p-6 flex flex-col items-center justify-center"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
>
<h3 className="text-white text-xl font-bold mb-6 text-center">
{member.name}
</h3>
<div className="flex flex-col gap-4 w-full">
{member.email && (
<a
href={`mailto:${member.email}`}
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200 transform hover:scale-105"
onClick={(e) => e.stopPropagation()}
>
<Mail size={20} />
<span className="text-sm">Email</span>
</a>
)}
{member.linkedin && (
<a
href={member.linkedin}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200 transform hover:scale-105"
onClick={(e) => e.stopPropagation()}
>
<Linkedin size={20} />
<span className="text-sm">LinkedIn</span>
</a>
)}
{member.twitter && (
<a
href={member.twitter}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200 transform hover:scale-105"
onClick={(e) => e.stopPropagation()}
>
<Twitter size={20} />
<span className="text-sm">Twitter</span>
</a>
)}
{member.github && (
<a
href={member.github}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200 transform hover:scale-105"
onClick={(e) => e.stopPropagation()}
>
<Github size={20} />
<span className="text-sm">GitHub</span>
</a>
)}
</div>
<p className="text-white text-xs mt-6 text-center opacity-75">
Click to flip back
</p>
</div>
</div>
<h3 className={cls("relative z-1 w-8/10 text-2xl font-medium leading-tight truncate", useInvertedBackground ? "text-background" : "text-foreground", nameClassName)}>
{member.name}
</h3>
<p className={cls("relative z-1 w-8/10 text-base leading-tight mt-1 truncate", useInvertedBackground ? "text-background/75" : "text-foreground/75", roleClassName)}>
{member.role}
</p>
</div>
))}
</div>
</div>
</section>
);
};
TeamCardFive.displayName = "TeamCardFive";
export default TeamCardFive;
}

View File

@@ -44,29 +44,27 @@ export default function TestimonialCardTwelve({
];
return (
<section className={`py-16 px-4 ${useInvertedBackground ? 'bg-gray-900' : 'bg-white'}`}>
<section className={`py-16 px-4 ${useInvertedBackground ? 'bg-gray-900 text-white' : 'bg-white text-gray-900'}`}>
<div className="max-w-6xl mx-auto">
<div className="text-center mb-12">
<div className="flex items-center justify-center gap-2 mb-4">
{TagIcon && <TagIcon className="w-5 h-5 text-blue-600" />}
<span className="text-sm font-semibold text-blue-600 uppercase tracking-wide">
{cardTag}
</span>
{TagIcon && <TagIcon className="w-5 h-5" />}
<span className="text-sm font-semibold text-blue-600">{cardTag}</span>
</div>
<h2 className={`text-3xl md:text-4xl font-bold ${useInvertedBackground ? 'text-white' : 'text-gray-900'}`}>
{cardTitle}
</h2>
<h2 className="text-3xl md:text-4xl font-bold mb-4">{cardTitle}</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{testimonials.map((testimonial) => (
<div
key={testimonial.id}
className={`h-80 cursor-pointer perspective ${cardAnimation === 'slide-up' ? 'animate-slide-up' : ''}`}
className="h-80 cursor-pointer perspective"
onClick={() => toggleFlip(testimonial.id)}
>
<div
className="relative w-full h-full transition-transform duration-500 transform-gpu"
className={`relative w-full h-full transition-transform duration-500 transform-gpu ${
flipped[testimonial.id] ? '[transform:rotateY(180deg)]' : ''
}`}
style={{
transformStyle: 'preserve-3d',
transform: flipped[testimonial.id] ? 'rotateY(180deg)' : 'rotateY(0deg)',
@@ -74,10 +72,8 @@ export default function TestimonialCardTwelve({
>
{/* Front of card */}
<div
className="absolute w-full h-full bg-white rounded-lg shadow-lg overflow-hidden"
style={{
backfaceVisibility: 'hidden',
}}
className="absolute w-full h-full bg-gray-100 rounded-lg overflow-hidden shadow-lg"
style={{ backfaceVisibility: 'hidden' }}
>
<div className="relative w-full h-full">
<Image
@@ -100,9 +96,7 @@ export default function TestimonialCardTwelve({
transform: 'rotateY(180deg)',
}}
>
<h3 className="text-white text-lg font-semibold mb-6 text-center">
{testimonial.name}
</h3>
<h3 className="text-white text-lg font-semibold mb-6 text-center">{testimonial.name}</h3>
<div className="flex flex-col gap-4 w-full">
{socialLinks.map((link) => (
<a
@@ -116,33 +110,13 @@ export default function TestimonialCardTwelve({
</a>
))}
</div>
<p className="text-white/70 text-xs mt-6 text-center">Click to flip back</p>
</div>
</div>
</div>
))}
</div>
</div>
<style jsx>{`
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.animate-slide-up {
animation: slide-up 0.6s ease-out forwards;
}
.perspective {
perspective: 1000px;
}
`}</style>
</section>
);
}

View File

@@ -57,131 +57,122 @@ const defaultMembers: TeamMember[] = [
},
];
export default function Tag({ members = defaultMembers }: TagProps) {
const [flipped, setFlipped] = useState<{ [key: string]: boolean }>({});
const toggleFlip = (id: string) => {
setFlipped((prev) => ({
...prev,
[id]: !prev[id],
}));
};
function TeamCard({ member }: { member: TeamMember }) {
const [isFlipped, setIsFlipped] = useState(false);
return (
<section className="py-16 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-white to-gray-50">
<div className="max-w-7xl mx-auto">
<div
className="h-80 cursor-pointer perspective"
onMouseEnter={() => setIsFlipped(true)}
onMouseLeave={() => setIsFlipped(false)}
>
<div
className={`relative w-full h-full transition-transform duration-500 transform-gpu ${
isFlipped ? "[transform:rotateY(180deg)]" : ""
}`}
style={{
transformStyle: "preserve-3d",
transform: isFlipped ? "rotateY(180deg)" : "rotateY(0deg)",
}}
>
{/* Front of card */}
<div
className="absolute w-full h-full bg-white rounded-lg shadow-lg overflow-hidden flex flex-col items-center justify-center p-6"
style={{ backfaceVisibility: "hidden" }}
>
<img
src={member.image}
alt={member.name}
className="w-32 h-32 rounded-full object-cover mb-4"
/>
<h3 className="text-xl font-bold text-gray-900 text-center">
{member.name}
</h3>
<p className="text-sm text-gray-600 text-center mt-2">
{member.title}
</p>
</div>
{/* Back of card */}
<div
className="absolute w-full h-full bg-gradient-to-br from-blue-600 to-blue-800 rounded-lg shadow-lg overflow-hidden flex flex-col items-center justify-center p-6"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
>
<h3 className="text-white font-bold text-lg mb-6 text-center">
Connect
</h3>
<div className="flex flex-wrap gap-4 justify-center">
{member.email && (
<a
href={`mailto:${member.email}`}
className="bg-white bg-opacity-20 hover:bg-opacity-30 text-white p-3 rounded-full transition-all duration-200 transform hover:scale-110"
title="Email"
>
<Mail size={20} />
</a>
)}
{member.linkedin && (
<a
href={member.linkedin}
target="_blank"
rel="noopener noreferrer"
className="bg-white bg-opacity-20 hover:bg-opacity-30 text-white p-3 rounded-full transition-all duration-200 transform hover:scale-110"
title="LinkedIn"
>
<Linkedin size={20} />
</a>
)}
{member.twitter && (
<a
href={member.twitter}
target="_blank"
rel="noopener noreferrer"
className="bg-white bg-opacity-20 hover:bg-opacity-30 text-white p-3 rounded-full transition-all duration-200 transform hover:scale-110"
title="Twitter"
>
<Twitter size={20} />
</a>
)}
{member.github && (
<a
href={member.github}
target="_blank"
rel="noopener noreferrer"
className="bg-white bg-opacity-20 hover:bg-opacity-30 text-white p-3 rounded-full transition-all duration-200 transform hover:scale-110"
title="GitHub"
>
<Github size={20} />
</a>
)}
</div>
<p className="text-white text-xs mt-6 text-center opacity-75">
Hover to flip back
</p>
</div>
</div>
</div>
);
}
export default function Tag({ members = defaultMembers }: TagProps) {
return (
<section className="py-16 px-4 bg-gray-50">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
Meet Our Leadership
</h2>
<p className="text-lg text-gray-600">
Exceptional leaders driving innovation and excellence
Hover over each card to see social links
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{members.map((member) => (
<div
key={member.id}
className="h-80 cursor-pointer perspective"
onClick={() => toggleFlip(member.id)}
>
<div
className="relative w-full h-full transition-transform duration-500 transform-gpu"
style={{
transformStyle: "preserve-3d",
transform: flipped[member.id]
? "rotateY(180deg)"
: "rotateY(0deg)",
}}
>
{/* Front of card */}
<div
className="absolute w-full h-full bg-white rounded-lg shadow-lg overflow-hidden"
style={{ backfaceVisibility: "hidden" }}
>
<img
src={member.image}
alt={member.name}
className="w-full h-64 object-cover"
/>
<div className="p-6 text-center">
<h3 className="text-xl font-bold text-gray-900 mb-2">
{member.name}
</h3>
<p className="text-sm text-gray-600">{member.title}</p>
<p className="text-xs text-gray-400 mt-3">
Hover to see social links
</p>
</div>
</div>
{/* Back of card */}
<div
className="absolute w-full h-full bg-gradient-to-br from-blue-600 to-blue-800 rounded-lg shadow-lg p-6 flex flex-col justify-center items-center"
style={{
backfaceVisibility: "hidden",
transform: "rotateY(180deg)",
}}
>
<h3 className="text-white text-lg font-bold mb-6 text-center">
{member.name}
</h3>
<div className="flex flex-col gap-4 w-full">
{member.email && (
<a
href={`mailto:${member.email}`}
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
onClick={(e) => e.stopPropagation()}
>
<Mail size={18} />
<span className="text-sm">Email</span>
</a>
)}
{member.linkedin && (
<a
href={member.linkedin}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
onClick={(e) => e.stopPropagation()}
>
<Linkedin size={18} />
<span className="text-sm">LinkedIn</span>
</a>
)}
{member.twitter && (
<a
href={member.twitter}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
onClick={(e) => e.stopPropagation()}
>
<Twitter size={18} />
<span className="text-sm">Twitter</span>
</a>
)}
{member.github && (
<a
href={member.github}
target="_blank"
rel="noopener noreferrer"
className="flex items-center justify-center gap-3 bg-white bg-opacity-20 hover:bg-opacity-30 text-white py-2 px-4 rounded-lg transition-all duration-200"
onClick={(e) => e.stopPropagation()}
>
<Github size={18} />
<span className="text-sm">GitHub</span>
</a>
)}
</div>
</div>
</div>
</div>
<TeamCard key={member.id} member={member} />
))}
</div>
</div>