118 lines
4.0 KiB
TypeScript
118 lines
4.0 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[0], COLLECTIONS[1]];
|
|
const col2 = [COLLECTIONS[2], COLLECTIONS[3]];
|
|
const col3 = [COLLECTIONS[4], COLLECTIONS[5]];
|
|
|
|
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.div
|
|
className="flex justify-between items-center border-t border-gray-400/50 dark:border-gray-800 pt-4"
|
|
initial={{ opacity: 0.8 }}
|
|
whileHover={{ opacity: 1 }}
|
|
>
|
|
<h3 className="font-display text-3xl font-light text-text-main dark:text-white group-hover:italic transition-all duration-300">
|
|
{item.title}
|
|
</h3>
|
|
<motion.span
|
|
className="text-xs uppercase tracking-widest text-text-muted"
|
|
whileHover={{ x: 5 }}
|
|
transition={{ duration: 0.3 }}
|
|
>
|
|
{item.number}
|
|
</motion.span>
|
|
</motion.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-1 md:grid-cols-2 lg:grid-cols-3 gap-8 lg:gap-16">
|
|
<div className="flex flex-col space-y-16 md:space-y-32">
|
|
{col1.map((item, idx) => renderCard(item, idx))}
|
|
</div>
|
|
<div className="flex flex-col space-y-16 md:space-y-32 md:pt-32">
|
|
{col2.map((item, idx) => renderCard(item, idx + 2))}
|
|
</div>
|
|
<div className="flex flex-col space-y-16 md:space-y-32 md:pt-16 lg:pt-0">
|
|
{col3.map((item, idx) => renderCard(item, idx + 4))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default Collections; |