52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { motion } from "framer-motion";
|
|
import type { ReactNode } from "react";
|
|
|
|
interface BentoCardProps {
|
|
title: string;
|
|
icon: ReactNode;
|
|
span?: string;
|
|
children: ReactNode;
|
|
className?: string;
|
|
}
|
|
|
|
// Convert desktop spans like "col-span-2 row-span-1" into responsive spans
|
|
// On mobile (1 col): everything is col-span-1
|
|
// On tablet (2 cols): col-span-2 stays, col-span-1 stays
|
|
// On desktop (4 cols): original spans
|
|
function responsiveSpan(span: string): string {
|
|
return span
|
|
.replace("col-span-2", "sm:col-span-2 lg:col-span-2 col-span-1")
|
|
.replace("row-span-2", "sm:row-span-2 lg:row-span-2 row-span-1")
|
|
.replace("col-span-1", "col-span-1")
|
|
.replace("row-span-1", "row-span-1");
|
|
}
|
|
|
|
export function BentoCard({
|
|
title,
|
|
icon,
|
|
span = "col-span-1 row-span-1",
|
|
children,
|
|
className = "",
|
|
}: BentoCardProps) {
|
|
const responsive = responsiveSpan(span);
|
|
|
|
return (
|
|
<motion.div
|
|
className={`glass-card flex flex-col overflow-hidden p-4 ${responsive} ${className}`}
|
|
// ponytail: drop the hover scale — 12 cards bobs noticeably when the
|
|
// cursor moves. The CSS .glass-card:hover (border + glow) is enough.
|
|
whileTap={{ scale: 0.985 }}
|
|
transition={{ type: "spring", stiffness: 400, damping: 25 }}
|
|
>
|
|
{/* Header */}
|
|
<div className="flex items-center gap-2 pb-2 mb-3 border-b border-[var(--color-border-glass)]">
|
|
<span className="text-[var(--color-gold-bright)]">{icon}</span>
|
|
<h2 className="font-heading text-sm font-semibold tracking-wide text-[var(--color-text-primary)]">
|
|
{title}
|
|
</h2>
|
|
</div>
|
|
{/* Body */}
|
|
<div className="flex-1 overflow-y-auto">{children}</div>
|
|
</motion.div>
|
|
);
|
|
} |