106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
import React from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { COLLECTIONS } from '../constants';
|
|
import { CollectionItem } from '../types';
|
|
|
|
const cardVariants = {
|
|
hidden: {
|
|
opacity: 0,
|
|
y: 80,
|
|
rotateX: 15,
|
|
},
|
|
visible: (index: number) => ({
|
|
opacity: 1,
|
|
y: 0,
|
|
rotateX: 0,
|
|
transition: {
|
|
delay: index * 0.15,
|
|
duration: 0.8,
|
|
ease: [0.25, 0.46, 0.45, 0.94],
|
|
},
|
|
}),
|
|
};
|
|
|
|
const Collections: React.FC = () => {
|
|
const col1 = COLLECTIONS.filter((_, i) => i % 4 === 0);
|
|
const col2 = COLLECTIONS.filter((_, i) => i % 4 === 1);
|
|
const col3 = COLLECTIONS.filter((_, i) => i % 4 === 2);
|
|
const col4 = COLLECTIONS.filter((_, i) => i % 4 === 3);
|
|
|
|
const renderCard = (item: CollectionItem, index: number) => (
|
|
<motion.a
|
|
key={item.id}
|
|
className="group block cursor-pointer"
|
|
href="#"
|
|
variants={cardVariants}
|
|
initial="hidden"
|
|
whileInView="visible"
|
|
viewport={{ once: true, margin: "-100px" }}
|
|
custom={index}
|
|
>
|
|
<div className={`relative overflow-hidden ${item.aspectRatio} mb-6`}>
|
|
{/* Image with clean hover effect */}
|
|
<motion.img
|
|
alt={`${item.title} collection`}
|
|
className="w-full h-full object-cover"
|
|
src={item.image}
|
|
whileHover={{ scale: 1.05 }}
|
|
transition={{ duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }}
|
|
/>
|
|
{/* Subtle overlay that fades out on hover */}
|
|
<motion.div
|
|
className="absolute inset-0 bg-black/5"
|
|
initial={{ opacity: 1 }}
|
|
whileHover={{ opacity: 0 }}
|
|
transition={{ duration: 0.4 }}
|
|
/>
|
|
{/* Clean reveal line effect on hover */}
|
|
<motion.div
|
|
className="absolute bottom-0 left-0 right-0 h-1 bg-white/80"
|
|
initial={{ scaleX: 0 }}
|
|
whileHover={{ scaleX: 1 }}
|
|
transition={{ duration: 0.5, ease: "easeOut" }}
|
|
style={{ transformOrigin: "left" }}
|
|
/>
|
|
</div>
|
|
</motion.a>
|
|
);
|
|
|
|
return (
|
|
<section className="py-32 bg-warm-grey dark:bg-[#141210] transition-colors duration-500">
|
|
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
|
<motion.div
|
|
className="flex flex-col md:flex-row justify-between items-end mb-20 md:mb-32 px-4"
|
|
initial={{ opacity: 0, y: 40 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.8, ease: "easeOut" }}
|
|
>
|
|
<h2 className="font-display text-5xl md:text-7xl font-thin text-text-main dark:text-white">
|
|
Curated <span className="italic text-text-muted">Editions</span>
|
|
</h2>
|
|
<p className="hidden md:block font-body text-sm text-text-muted max-w-xs leading-relaxed text-right">
|
|
Explore our seasonal collections, fired in small batches.
|
|
</p>
|
|
</motion.div>
|
|
|
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-8 lg:gap-12">
|
|
<div className="flex flex-col space-y-8 md:space-y-16">
|
|
{col1.map((item, idx) => renderCard(item, idx))}
|
|
</div>
|
|
<div className="flex flex-col space-y-8 md:space-y-16 pt-16 md:pt-24">
|
|
{col2.map((item, idx) => renderCard(item, idx + 2))}
|
|
</div>
|
|
<div className="flex flex-col space-y-8 md:space-y-16 pt-8 md:pt-12 lg:pt-0">
|
|
{col3.map((item, idx) => renderCard(item, idx + 4))}
|
|
</div>
|
|
<div className="flex flex-col space-y-8 md:space-y-16 pt-24 md:pt-32 lg:pt-24">
|
|
{col4.map((item, idx) => renderCard(item, idx + 6))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default Collections; |