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.
131 lines
6.0 KiB
TypeScript
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; |