This commit is contained in:
2026-03-23 19:00:17 -05:00
parent f0c19fbbfa
commit 92676e652a
94 changed files with 9558 additions and 6871 deletions

View File

@@ -0,0 +1,176 @@
import React, { useEffect, useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
declare global {
interface Window {
instgrm?: {
Embeds: {
process: () => void;
};
};
}
}
const posts = [
'https://www.instagram.com/p/DSOFijljukL/',
'https://www.instagram.com/p/DSGh7rMjVes/',
'https://www.instagram.com/p/DRIIUECj4f6/',
'https://www.instagram.com/p/DJOvmvcIoo7/',
'https://www.instagram.com/p/DJOu5b7IL4t/',
'https://www.instagram.com/p/DIQnhO0oJgw/',
'https://www.instagram.com/p/DIJUVqbI4EH/',
'https://www.instagram.com/p/DHlvDNyIDRa/',
'https://www.instagram.com/p/DHlub_iojwv/',
'https://www.instagram.com/p/DJOvdVLIZpM/',
];
const InstagramFeed: React.FC = () => {
const [selectedPost, setSelectedPost] = useState<string | null>(null);
// Double the list for seamless infinite marquee scroll
const duplicatedPosts = [...posts, ...posts];
useEffect(() => {
// Process Instagram embeds whenever the component mounts or the lightbox opens
if (window.instgrm) {
window.instgrm.Embeds.process();
} else {
const script = document.createElement('script');
script.src = '//www.instagram.com/embed.js';
script.async = true;
document.body.appendChild(script);
}
}, [selectedPost]);
return (
<>
<section className="py-24 px-6 md:px-12 bg-stone-50 dark:bg-stone-900 overflow-hidden">
<div className="max-w-[1400px] mx-auto">
<span className="block font-body text-xs uppercase tracking-[0.3em] text-stone-400 mb-6">
Follow Along
</span>
<div className="flex items-end justify-between mb-16 px-2">
<h2 className="font-display text-4xl md:text-5xl text-text-main dark:text-white leading-none">
From the Studio
</h2>
<a
href="https://www.instagram.com/knuth.ceramics"
target="_blank"
rel="noopener noreferrer"
className="text-xs font-bold uppercase tracking-widest text-stone-500 hover:text-stone-900 dark:hover:text-white transition-colors"
>
@knuth.ceramics
</a>
</div>
{/* Infinite Carousel */}
<div className="relative group overflow-hidden">
<style>{`
@keyframes marquee {
0% { transform: translateX(0); }
100% { transform: translateX(-${posts.length * 342}px); /* 326px width + 16px gap */ }
}
.animate-marquee {
animation: marquee 50s linear infinite;
}
.animate-marquee:hover {
animation-play-state: paused;
}
`}</style>
<div className="flex gap-4 animate-marquee w-max py-4">
{duplicatedPosts.map((permalink, idx) => (
<div
key={idx}
className="relative flex-shrink-0 w-[326px] overflow-hidden rounded-[8px] group/item cursor-pointer bg-white"
>
{/* Invisible Overlay to capture clicks.
Because iframes block events, we put a div above it.
On hover it reveals a subtle mask to indicate interactivity. */}
<div
className="absolute inset-0 z-10 bg-black/0 group-hover/item:bg-black/50 transition-colors duration-300 flex flex-col items-center justify-center opacity-0 group-hover/item:opacity-100"
onClick={() => setSelectedPost(permalink)}
>
<p className="text-white font-display text-lg px-4 text-center font-bold drop-shadow-md">
View Post
</p>
</div>
{/* The Instagram Embed itself.
By omitting data-instgrm-captioned we hide the caption/hashtags directly. */}
<div className="pointer-events-none">
<blockquote
className="instagram-media"
data-instgrm-permalink={`${permalink}?utm_source=ig_embed&utm_campaign=loading`}
data-instgrm-version="14"
style={{
background: '#FFF',
border: 0,
margin: 0,
maxWidth: '540px',
minWidth: '326px',
padding: 0,
width: '100%',
}}
/>
</div>
</div>
))}
</div>
</div>
</div>
</section>
{/* Lightbox Modal */}
<AnimatePresence>
{selectedPost && (
<motion.div
className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center p-4 overflow-y-auto pt-[100px]"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedPost(null)}
>
<motion.div
className="relative max-w-lg w-full bg-white dark:bg-stone-900 rounded-xl overflow-hidden my-auto"
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
onClick={(e) => e.stopPropagation()}
>
{/* Close button inside modal container */}
<button
className="absolute top-2 right-2 text-stone-500 bg-white border border-stone-200 shadow-md rounded-full w-8 h-8 flex items-center justify-center hover:bg-stone-100 transition-colors z-[60]"
onClick={() => setSelectedPost(null)}
>
<span className="font-bold">×</span>
</button>
{/* Instagram Embed WITH caption shown in the Lightbox */}
<div className="w-full bg-white mt-12 pb-4 px-2">
<blockquote
className="instagram-media"
data-instgrm-captioned
data-instgrm-permalink={`${selectedPost}?utm_source=ig_embed&utm_campaign=loading`}
data-instgrm-version="14"
style={{
background: '#FFF',
border: 0,
boxShadow: 'none',
margin: '0',
maxWidth: '540px',
minWidth: '326px',
padding: 0,
width: '100%',
}}
/>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
);
};
export default InstagramFeed;