Files
bayarea/components/Blog.tsx
knuthtimo-lab 1a85f0eb2d feat: Initialize project with Vite and React
Sets up the project structure with Vite, React, and essential libraries like GSAP and Framer Motion. Configures Tailwind CSS for styling, including dark mode and custom color schemes. Includes basic component structure for the application, laying the groundwork for UI development.
2026-01-15 18:27:22 +01:00

131 lines
6.0 KiB
TypeScript

import React, { useRef, useLayoutEffect } from 'react';
import { motion } from 'framer-motion';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const posts = [
{
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuARalmRkuoZMBAbavGQgx4a-JhLgXBJ6JSD0U4vycdwaGGV3d-ffUFrdbx2lIbKrYCmS100i7VJ0w5cDHITXYV6w1-pSUPHKL7Jik__TWOIYOnq_4ND5ri7l8SQoaJdjJK9jhYvtxdxrZm6j8t8BNAjvPTaUdUDo4C7QVqcx1KbGvup6cpF8vY1LJ82S_5OMAZ6JgH0rK5bvWpqD3WqPhtqJCUB6d_1gUvluKjotwnNQ03t1dSYV8HOtRrLE83j6i_wgL4GZ0XTsMZb',
date: 'Oct 12, 2024',
category: 'Cybersecurity',
title: 'The Hidden Risks of Remote Work',
excerpt: 'As remote work becomes permanent, new vulnerabilities emerge. Learn how to secure your distributed workforce effectively.'
},
{
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCz5lTYjY4RNXubQlrA-BtLIGR3nUY8ULkD9omwT5FShfdMrbMgS5dDCyfN3xiB5WC7T3vjNvyvVbvnD0G1zBpbNTjfOYyhmAEfno7Hf5W1sm-KYRXYrLGQq-c6TkLgEf0i9JGNvuFZ6edcenr2o39dCzIPXcp_z9XWOIzp7kBX2EydNPLJoRofVYuSTmEA1y0_xh4sdiRy1PykRASGLhKfN19_XLNuwyTBVKYISY7cHc-An69eZpAfhrvngu3E47rU6KuQS0k3QXBZ',
date: 'Sep 28, 2024',
category: 'Cloud Infrastructure',
title: 'Migrating to the Cloud: A Step-by-Step Guide',
excerpt: 'Thinking about moving your data? Here is a comprehensive checklist to ensure a smooth and secure transition.'
},
{
image: 'https://lh3.googleusercontent.com/aida-public/AB6AXuCl5iOhTsCqcHnho89DkoLh0DYeuvef0pdp8k26NKzcAq7YPvWbAYARg9mCIvqGTxQGradp8zvscuuibskpz4W_nEzQQO1z7lgwKJ1Xxiw_yQOyXMLfoRNLTHXzqFUH8Q5daCAfYTb7Zl3sFjB7k8i44D6TGolzqrN05Db27Abf2TWDDzHpVSrNml4zddvxholHFxMzqDeSzQ5p77SLDSFNaYBZGR2lEdN2V9O0GzMqxbOjFmBGMW48nlrEDLDzYGv_gWI3RSqNqBl-',
date: 'Sep 15, 2024',
category: 'Innovation',
title: 'AI in Business: Beyond the Hype',
excerpt: 'Artificial Intelligence is transforming industries. Discover practical applications that can drive efficiency in your business today.'
}
];
const Blog: React.FC = () => {
const containerRef = useRef<HTMLDivElement>(null);
const imagesRef = useRef<(HTMLDivElement | null)[]>([]);
imagesRef.current = [];
useLayoutEffect(() => {
const ctx = gsap.context(() => {
imagesRef.current.forEach((imgWrapper) => {
if (!imgWrapper) return;
gsap.to(imgWrapper, {
yPercent: 30,
ease: "none",
scrollTrigger: {
trigger: imgWrapper.closest('article'),
start: "top bottom",
end: "bottom top",
scrub: true
}
});
});
}, containerRef);
return () => ctx.revert();
}, []);
return (
<motion.section
ref={containerRef}
id="blog"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="py-24 bg-background-light dark:bg-background-dark border-t border-gray-200 dark:border-white/10"
>
<div className="max-w-7xl mx-auto px-6">
<div className="flex justify-between items-end mb-12">
<div>
<span className="text-xs font-semibold uppercase tracking-widest text-gray-500 dark:text-gray-500 mb-2 block">Latest Insights</span>
<h2 className="font-display text-3xl md:text-4xl text-gray-900 dark:text-white">
Knowledge <span className="text-gray-400 dark:text-gray-600">base.</span>
</h2>
</div>
<motion.a
href="#"
className="hidden md:inline-flex items-center text-sm font-medium text-gray-900 dark:text-white hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
whileHover={{ x: 5 }}
>
View all posts <span className="material-symbols-outlined text-sm ml-1">arrow_forward</span>
</motion.a>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{posts.map((post, i) => (
<motion.article
key={i}
initial={{ opacity: 0, scale: 0.95 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: i * 0.1 }}
whileHover={{ y: -8 }}
className="group cursor-pointer"
>
<div className="h-64 rounded-xl overflow-hidden mb-6 relative shadow-lg">
<div
ref={el => { if(el) imagesRef.current.push(el); }}
className="w-full h-[140%] -mt-[20%]"
>
<img
src={post.image}
alt={post.title}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
/>
</div>
<div className="absolute inset-0 bg-black/20 group-hover:bg-black/10 transition-colors pointer-events-none"></div>
<div className="absolute top-4 right-4 bg-white/90 dark:bg-black/80 backdrop-blur text-xs font-bold px-3 py-1 rounded-full uppercase tracking-wider z-10">
Read
</div>
</div>
<div className="flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400 mb-3">
<span>{post.date}</span>
<span className="w-1 h-1 rounded-full bg-gray-400"></span>
<span className="text-blue-600 dark:text-blue-400 font-medium">{post.category}</span>
</div>
<h3 className="text-xl font-display font-bold text-gray-900 dark:text-white mb-2 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
{post.title}
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
{post.excerpt}
</p>
</motion.article>
))}
</div>
</div>
</motion.section>
);
};
export default Blog;