Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 5s
Build & Deploy / 🏗️ Build (push) Failing after 14s
Build & Deploy / 🧪 QA (push) Failing after 1m48s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
257 lines
14 KiB
TypeScript
257 lines
14 KiB
TypeScript
'use client';
|
|
|
|
import React from 'react';
|
|
import { ComponentShareButton } from './ComponentShareButton';
|
|
import { Reveal } from './Reveal';
|
|
|
|
interface MemeCardProps {
|
|
/** Meme template type: drake, ds (daily struggle), gru, fine, clown, expanding, distracted, rollsafe */
|
|
template: string;
|
|
/** Pipe-delimited captions */
|
|
captions: string;
|
|
/** Optional local image path. If provided, overrides the text-based template. */
|
|
image?: string;
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* Premium text-based meme cards with dedicated layouts per template.
|
|
* Uses emoji + typography instead of images for on-brand aesthetics.
|
|
*/
|
|
export const MemeCard: React.FC<MemeCardProps> = ({ template, captions, image, className = '' }) => {
|
|
const captionList = (captions || '').split('|').map(s => s.trim()).filter(Boolean);
|
|
const shareId = `meme-${Math.random().toString(36).substring(7).toUpperCase()}`;
|
|
|
|
if (image) {
|
|
return (
|
|
<Reveal direction="up" delay={0.1}>
|
|
<div id={shareId} className={`not-prose max-w-xl mx-auto my-12 group relative transition-all duration-500 ease-out z-10 ${className}`}>
|
|
<div className="absolute -inset-1 bg-gradient-to-r from-slate-200 to-slate-100 rounded-[2rem] blur opacity-10 group-hover:opacity-30 transition duration-1000 -z-10" />
|
|
|
|
<div className="glass bg-white/80 backdrop-blur-xl border border-slate-100 rounded-2xl overflow-hidden shadow-sm shadow-slate-200 group-hover:shadow-md transition-all duration-500 relative">
|
|
<div className="absolute top-4 right-4 md:opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-50">
|
|
<ComponentShareButton targetId={shareId} title={`Meme: ${template}`} />
|
|
</div>
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img
|
|
src={image}
|
|
alt={`Meme: ${template} - ${captionList.join(' ')}`}
|
|
className="w-full h-auto object-cover block"
|
|
loading="lazy"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Reveal direction="up" delay={0.1}>
|
|
<div id={shareId} className={`not-prose max-w-xl mx-auto my-12 group relative transition-all duration-500 ease-out z-10 ${className}`}>
|
|
<div className="absolute -inset-1 bg-gradient-to-r from-slate-200 to-slate-100 rounded-[2rem] blur opacity-10 group-hover:opacity-30 transition duration-1000 -z-10" />
|
|
|
|
<div className="glass bg-white/80 backdrop-blur-xl border border-slate-100 rounded-2xl overflow-hidden shadow-sm shadow-slate-200 group-hover:shadow-md transition-all duration-500 relative">
|
|
<div className="absolute top-4 right-4 md:opacity-0 group-hover:opacity-100 transition-opacity duration-500 z-50">
|
|
<ComponentShareButton targetId={shareId} title={`Meme: ${template}`} />
|
|
</div>
|
|
|
|
{template === 'drake' && <DrakeMeme captions={captionList} />}
|
|
{template === 'ds' && <DailyStruggleMeme captions={captionList} />}
|
|
{template === 'gru' && <GruMeme captions={captionList} />}
|
|
{template === 'fine' && <FineMeme captions={captionList} />}
|
|
{template === 'clown' && <ClownMeme captions={captionList} />}
|
|
{template === 'expanding' && <ExpandingBrainMeme captions={captionList} />}
|
|
{template === 'distracted' && <DistractedMeme captions={captionList} />}
|
|
{!['drake', 'ds', 'gru', 'fine', 'clown', 'expanding', 'distracted'].includes(template) && (
|
|
<GenericMeme captions={captionList} template={template} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
);
|
|
};
|
|
|
|
function DrakeMeme({ captions }: { captions: string[] }) {
|
|
return (
|
|
<div className="flex flex-col">
|
|
<div className="flex items-stretch border-b border-slate-100">
|
|
<div className="w-20 md:w-24 bg-red-400/10 flex items-center justify-center flex-shrink-0 border-r border-slate-100">
|
|
<span className="text-3xl md:text-4xl select-none grayscale-0 group-hover:scale-110 transition-transform duration-500">🙅</span>
|
|
</div>
|
|
<div className="flex-1 p-5 md:p-6 flex items-center bg-white/40">
|
|
<p className="text-lg md:text-xl font-medium text-slate-500 leading-snug">{captions[0]}</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-stretch">
|
|
<div className="w-20 md:w-24 bg-emerald-400/10 flex items-center justify-center flex-shrink-0 border-r border-slate-100">
|
|
<span className="text-3xl md:text-4xl select-none group-hover:scale-110 transition-transform duration-500">😎</span>
|
|
</div>
|
|
<div className="flex-1 p-5 md:p-6 flex items-center bg-white">
|
|
<p className="text-lg md:text-xl font-bold text-slate-900 leading-snug">{captions[1]}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function DailyStruggleMeme({ captions }: { captions: string[] }) {
|
|
return (
|
|
<div className="p-8 md:p-10 text-center">
|
|
<div className="text-4xl md:text-5xl mb-6 select-none animate-bounce-subtle">😰</div>
|
|
<p className="text-[10px] font-black text-slate-300 uppercase tracking-[0.3em] mb-8">Daily Struggle</p>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="p-5 rounded-2xl bg-white border border-slate-100 shadow-sm hover:border-slate-200 transition-colors">
|
|
<div className="w-5 h-5 rounded-full bg-red-500 mx-auto mb-3 shadow-[0_0_10px_rgba(239,68,68,0.4)]" />
|
|
<p className="text-sm md:text-base font-bold text-slate-700 leading-snug">{captions[0]}</p>
|
|
</div>
|
|
<div className="p-5 rounded-2xl bg-white border border-slate-100 shadow-sm hover:border-slate-200 transition-colors">
|
|
<div className="w-5 h-5 rounded-full bg-red-500 mx-auto mb-3 shadow-[0_0_10px_rgba(239,68,68,0.4)]" />
|
|
<p className="text-sm md:text-base font-bold text-slate-700 leading-snug">{captions[1]}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function GruMeme({ captions }: { captions: string[] }) {
|
|
const steps = captions.slice(0, 4);
|
|
return (
|
|
<div className="grid grid-cols-2 grid-rows-2">
|
|
{(steps || []).map((caption, i) => {
|
|
const isLast = i >= 2;
|
|
return (
|
|
<div
|
|
key={i}
|
|
className={`p-6 md:p-8 ${i % 2 === 0 ? 'border-r' : ''} ${i < 2 ? 'border-b' : ''} border-slate-100 flex flex-col items-center justify-center text-center gap-3 transition-colors hover:bg-slate-50/30`}
|
|
>
|
|
<span className="text-2xl md:text-3xl select-none transition-transform group-hover:scale-110">
|
|
{isLast ? '😱' : '😏'}
|
|
</span>
|
|
<p className={`text-base md:text-lg leading-tight ${isLast ? 'font-black text-red-500' : 'font-bold text-slate-700'}`}>
|
|
{caption}
|
|
</p>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function FineMeme({ captions }: { captions: string[] }) {
|
|
return (
|
|
<div className="flex flex-col">
|
|
<div className="bg-orange-50/50 border-b border-slate-100 p-6 md:p-8">
|
|
<div className="flex items-center gap-3 mb-4">
|
|
<span className="text-3xl md:text-4xl select-none">🔥</span>
|
|
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest m-0">This is Fine</p>
|
|
</div>
|
|
<p className="text-lg md:text-xl font-bold text-slate-700 leading-snug">{captions[0]}</p>
|
|
</div>
|
|
<div className="p-6 md:p-8 bg-white">
|
|
<div className="flex items-center gap-4">
|
|
<span className="text-3xl select-none group-hover:rotate-12 transition-transform">☕</span>
|
|
<p className="text-lg md:text-2xl font-black text-slate-900 leading-tight italic tracking-tight">
|
|
“{captions[1] || 'Alles im grünen Bereich.'}”
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ClownMeme({ captions }: { captions: string[] }) {
|
|
const steps = captions.slice(0, 4);
|
|
const emojis = ['😐', '🤡', '💀', '🎪'];
|
|
return (
|
|
<div className="flex flex-col">
|
|
<div className="p-4 md:p-5 border-b border-slate-100 bg-slate-50/50">
|
|
<p className="text-[10px] font-black text-slate-300 uppercase tracking-[0.3em] m-0 text-center">Clown Progression</p>
|
|
</div>
|
|
{steps.map((caption, i) => (
|
|
<div
|
|
key={i}
|
|
className={`flex items-center gap-5 p-5 md:p-6 ${i < steps.length - 1 ? 'border-b border-slate-100' : ''} hover:bg-slate-50 transition-colors`}
|
|
>
|
|
<span className="text-2xl md:text-3xl select-none flex-shrink-0 grayscale opacity-60 group-hover:grayscale-0 group-hover:opacity-100 transition-all duration-500">{emojis[i] || '🤡'}</span>
|
|
<p className={`text-base md:text-lg leading-snug ${i === steps.length - 1 ? 'font-black text-red-500' : 'font-bold text-slate-700'}`}>
|
|
{caption}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ExpandingBrainMeme({ captions }: { captions: string[] }) {
|
|
const steps = captions.slice(0, 4);
|
|
const emojis = ['🧠', '🧠✨', '🧠💡', '🧠🚀'];
|
|
const shadows = [
|
|
'',
|
|
'shadow-[0_0_15px_rgba(59,130,246,0.1)]',
|
|
'shadow-[0_0_20px_rgba(99,102,241,0.2)]',
|
|
'shadow-[0_0_25px_rgba(168,85,247,0.3)]',
|
|
];
|
|
return (
|
|
<div className="flex flex-col">
|
|
<div className="p-4 md:p-5 border-b border-slate-100 bg-slate-50/50">
|
|
<p className="text-[10px] font-black text-slate-300 uppercase tracking-[0.3em] m-0 text-center">Expanding Intelligence</p>
|
|
</div>
|
|
{steps.map((caption, i) => (
|
|
<div
|
|
key={i}
|
|
className={`flex items-center gap-5 p-5 md:p-6 ${i < steps.length - 1 ? 'border-b border-slate-100' : ''} hover:bg-white transition-all duration-500 ${shadows[i]}`}
|
|
>
|
|
<span className="text-2xl md:text-3xl select-none flex-shrink-0 group-hover:scale-125 transition-transform duration-700">{emojis[i]}</span>
|
|
<p className={`text-base md:text-lg leading-tight ${i === steps.length - 1 ? 'font-black text-indigo-600' : 'font-bold text-slate-700'}`}>
|
|
{caption}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function DistractedMeme({ captions }: { captions: string[] }) {
|
|
return (
|
|
<div className="flex flex-col">
|
|
<div className="p-4 md:p-5 border-b border-slate-100 bg-slate-50/50">
|
|
<p className="text-[10px] font-black text-slate-300 uppercase tracking-[0.3em] m-0 text-center">The Distraction</p>
|
|
</div>
|
|
<div className="grid grid-cols-3 divide-x divide-slate-100">
|
|
<div className="p-6 md:p-8 flex flex-col items-center text-center gap-3 hover:bg-slate-50/50 transition-colors">
|
|
<span className="text-3xl md:text-4xl select-none">👤</span>
|
|
<p className="text-[9px] font-black text-slate-400 uppercase tracking-[0.2em] m-0">Subject</p>
|
|
<p className="text-sm md:text-base font-bold text-slate-500 leading-tight">{captions[0]}</p>
|
|
</div>
|
|
<div className="p-6 md:p-8 flex flex-col items-center text-center gap-3 bg-emerald-50/30 hover:bg-emerald-50/60 transition-colors">
|
|
<span className="text-3xl md:text-4xl select-none animate-pulse">✨</span>
|
|
<p className="text-[9px] font-black text-emerald-500 uppercase tracking-[0.2em] m-0">Temptation</p>
|
|
<p className="text-sm md:text-base font-black text-slate-900 leading-tight">{captions[1]}</p>
|
|
</div>
|
|
<div className="p-6 md:p-8 flex flex-col items-center text-center gap-3 bg-red-50/30 hover:bg-red-50/60 transition-colors">
|
|
<span className="text-3xl md:text-4xl select-none">😤</span>
|
|
<p className="text-[9px] font-black text-red-500 uppercase tracking-[0.2em] m-0">Reality</p>
|
|
<p className="text-sm md:text-base font-bold text-red-600 leading-tight">{captions[2]}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function GenericMeme({ captions, template }: { captions: string[]; template: string }) {
|
|
return (
|
|
<div className="p-8 md:p-12 text-center bg-gradient-to-br from-white to-slate-50/50">
|
|
<p className="text-[10px] font-black text-slate-400 uppercase tracking-[0.3em] mb-8">{template}</p>
|
|
<div className="space-y-4">
|
|
{(captions || []).map((caption, i) => (
|
|
<div key={i} className="p-4 md:p-5 bg-white border border-slate-100 rounded-2xl shadow-sm group-hover:border-slate-200 transition-all duration-300">
|
|
<p className="text-base md:text-lg font-bold text-slate-700 m-0">
|
|
{caption}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|