- Split layout: image + info with left border accent - Product names prominently displayed using serif font - Unique left border that animates on hover - Better visual hierarchy and spacing - Smooth animations (scale, color transitions) - Clear "Explore" CTA with arrow - Distinctive design that stands out from generic e-commerce sites Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
81 lines
2.8 KiB
TypeScript
81 lines
2.8 KiB
TypeScript
import Link from 'next/link';
|
|
import ProductMockup from './ProductMockup';
|
|
import Price from './Price';
|
|
import type { ProductDTO } from '@/lib/types';
|
|
|
|
const NEW_WINDOW_MS = 1000 * 60 * 60 * 24 * 30; // 30 days
|
|
|
|
export default function ProductCard({
|
|
product,
|
|
hrefBase = '/made-to-order',
|
|
}: {
|
|
product: ProductDTO;
|
|
hrefBase?: string;
|
|
}) {
|
|
const thumbnail = product.colorPhotos[product.colors[0]] ?? product.imageUrl;
|
|
const isNew = Date.now() - new Date(product.createdAt).getTime() < NEW_WINDOW_MS;
|
|
|
|
return (
|
|
<Link href={`${hrefBase}/${product.slug}`} className="group flex flex-col">
|
|
{/* Image Container - Top 2/3 */}
|
|
<div className="relative overflow-hidden bg-surface transition-all duration-500 group-hover:bg-ink/5">
|
|
<div className="aspect-square w-full flex items-center justify-center p-8">
|
|
{thumbnail ? (
|
|
<img
|
|
src={thumbnail}
|
|
alt={product.name}
|
|
className="h-full w-full object-contain transition-transform duration-500 group-hover:scale-110"
|
|
/>
|
|
) : (
|
|
<div className="transition-transform duration-500 group-hover:scale-110">
|
|
<ProductMockup mockup={product.mockup} color={product.colors[0]} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Badges */}
|
|
<div className="absolute inset-0 pointer-events-none">
|
|
{isNew && (
|
|
<span className="tag-label absolute left-4 top-4 bg-splash-blue text-paper px-3 py-1.5 text-xs font-medium">
|
|
New
|
|
</span>
|
|
)}
|
|
{product.onSale && (
|
|
<span className="tag-label absolute right-4 top-4 bg-splash-pink text-paper px-3 py-1.5 text-xs font-medium">
|
|
Sale
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Info Container - Bottom 1/3 */}
|
|
<div className="flex-1 flex flex-col border-l-4 border-l-clay bg-paper p-5 transition-all duration-300 group-hover:border-l-ink">
|
|
<h3 className="font-display text-lg leading-tight text-ink mb-2">
|
|
{product.name}
|
|
</h3>
|
|
|
|
{/* Price */}
|
|
<div className="mt-auto flex items-baseline gap-2">
|
|
{product.onSale && product.originalPrice != null && (
|
|
<Price
|
|
cents={product.originalPrice}
|
|
className="font-mono text-xs text-muted line-through"
|
|
/>
|
|
)}
|
|
<Price
|
|
cents={product.effectivePrice}
|
|
className={`font-mono font-medium ${product.onSale ? 'text-splash-pink' : 'text-ink'}`}
|
|
/>
|
|
</div>
|
|
|
|
{/* CTA */}
|
|
<div className="mt-4 text-center">
|
|
<span className="tag-label text-xs text-clay group-hover:text-ink transition-colors">
|
|
Explore →
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
);
|
|
}
|