113 lines
5.2 KiB
TypeScript
113 lines
5.2 KiB
TypeScript
import React, { useEffect } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import { Link } from 'react-router-dom';
|
|
import { useStore } from '../src/context/StoreContext';
|
|
|
|
interface BlogPostLayoutProps {
|
|
title: string;
|
|
category: string;
|
|
date: string;
|
|
image: string;
|
|
imageAlt: string;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const BlogPostLayout: React.FC<BlogPostLayoutProps> = ({
|
|
title,
|
|
category,
|
|
date,
|
|
image,
|
|
imageAlt,
|
|
children,
|
|
}) => {
|
|
const { articles } = useStore();
|
|
// Scroll to top on mount
|
|
useEffect(() => {
|
|
window.scrollTo(0, 0);
|
|
}, []);
|
|
|
|
const nextArticles = articles.filter(post => post.title !== title).slice(0, 2);
|
|
|
|
return (
|
|
<div className="bg-stone-50 dark:bg-black min-h-screen font-body transition-colors duration-500">
|
|
|
|
<main className="pt-32 pb-24">
|
|
{/* Article Header */}
|
|
<article className="max-w-4xl mx-auto px-6 md:px-12">
|
|
<div className="flex items-center space-x-4 mb-8 justify-center">
|
|
<span className="text-xs uppercase tracking-[0.2em] text-text-muted border border-text-muted/30 px-3 py-1 rounded-full">{category}</span>
|
|
<span className="text-xs uppercase tracking-[0.2em] text-text-muted">{date}</span>
|
|
</div>
|
|
|
|
<motion.h1
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.8 }}
|
|
className="font-display text-5xl md:text-6xl lg:text-7xl text-center text-text-main dark:text-white mb-16 leading-tight"
|
|
>
|
|
{title}
|
|
</motion.h1>
|
|
|
|
{/* Hero Image */}
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.95 }}
|
|
animate={{ opacity: 1, scale: 1 }}
|
|
transition={{ duration: 0.8, delay: 0.2 }}
|
|
className="w-full h-[40vh] md:h-[50vh] relative mb-16 overflow-hidden shadow-xl rounded-sm"
|
|
>
|
|
<img
|
|
src={image}
|
|
alt={imageAlt}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
</motion.div>
|
|
|
|
{/* Content Container */}
|
|
<div className="prose prose-stone dark:prose-invert max-w-none mx-auto prose-headings:font-display prose-headings:font-light prose-p:font-light prose-p:leading-loose prose-a:text-terracotta hover:prose-a:text-terracotta-dark prose-img:rounded-sm">
|
|
{children}
|
|
</div>
|
|
|
|
{/* Read Next Section */}
|
|
{nextArticles.length > 0 && (
|
|
<div className="mt-24 pt-16 border-t border-stone-200 dark:border-stone-800">
|
|
<h3 className="font-display text-3xl text-center mb-12 text-text-main dark:text-white">Read Next</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
{nextArticles.map((post) => (
|
|
<Link key={post.id} to={`/editorial/${post.slug}`} className="group block">
|
|
<div className="aspect-[3/2] overflow-hidden bg-stone-100 mb-4">
|
|
<img
|
|
src={post.image}
|
|
alt={post.title}
|
|
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
|
/>
|
|
</div>
|
|
<h4 className="font-display text-2xl text-text-main dark:text-white group-hover:underline decoration-1 underline-offset-4 mb-2">
|
|
{post.title}
|
|
</h4>
|
|
<div className="flex items-center space-x-2 text-sm text-stone-500 uppercase tracking-widest">
|
|
<span>{post.category}</span>
|
|
<span>—</span>
|
|
<span>{post.date}</span>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Back Link */}
|
|
<div className="mt-20 text-center">
|
|
<Link to="/editorial" className="inline-block border-b border-black dark:border-white pb-1 text-sm uppercase tracking-widest hover:text-stone-500 transition-colors">
|
|
Back to Editorial
|
|
</Link>
|
|
</div>
|
|
</article>
|
|
</main>
|
|
|
|
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default BlogPostLayout;
|