Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Mentors from './components/Mentors'
import SummerOfAI from './components/SummerOfAI'
import Contribute from './components/Contribute'
import Footer from './components/Footer'
import Blog from './components/Blog'

export default function App() {
return (
Expand All @@ -22,6 +23,7 @@ export default function App() {
<Storyboards />
<Mentors />
<SummerOfAI />
<Blog />
<Contribute />
</main>
<Footer />
Expand Down
76 changes: 76 additions & 0 deletions src/components/Blog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { useState } from 'react'
import { blogPosts } from '../data/blog'
import BlogPostLayout from './BlogPostLayout'

export default function Blog() {
const [selected, setSelected] = useState(null)

if (selected) {
return (
<section id="blog" className="py-16" style={{ background: 'var(--cream)' }}>
<div className="max-w-2xl mx-auto px-6">
<button
onClick={() => setSelected(null)}
className="flex items-center gap-2 text-sm text-[var(--ink-muted)] hover:text-[var(--purple)] mb-8 transition-colors"
>
← Back to Blog
</button>
</div>
<BlogPostLayout post={selected} />
</section>
)
}

return (
<section id="blog" className="py-20" style={{ background: 'var(--cream-dark)' }}>
<div className="max-w-5xl mx-auto px-6">
<p className="text-xs font-bold uppercase tracking-widest mb-3" style={{ color: 'var(--purple)' }}>
From the Community
</p>
<h2 className="font-display text-3xl md:text-4xl font-bold text-[var(--ink)] mb-12">
Blog & Resources
</h2>

<div className="grid md:grid-cols-3 gap-8">
{blogPosts.map((post) => (
<button
key={post.id}
onClick={() => setSelected(post)}
className="text-left group rounded-2xl overflow-hidden border border-[var(--border)] hover:border-[var(--purple)] transition-all duration-200 hover:shadow-lg"
style={{ background: 'white' }}
>
{/* Cover image — pre-sized */}
<div className="w-full aspect-[16/9] bg-[var(--cream-dark)] overflow-hidden">
<img
src={post.coverImage}
alt={post.title}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
width={400}
height={225}
/>
</div>

<div className="p-5">
<span
className="inline-block text-xs font-bold uppercase tracking-widest px-2 py-0.5 rounded-full mb-3"
style={{ background: 'var(--purple-light)', color: 'var(--purple)' }}
>
{post.category}
</span>
<h3 className="font-display text-lg font-bold text-[var(--ink)] leading-snug mb-2 group-hover:text-[var(--purple)] transition-colors">
{post.title}
</h3>
<p className="text-sm text-[var(--ink-muted)] line-clamp-2">{post.excerpt}</p>
<div className="flex items-center gap-2 mt-4 text-xs text-[var(--ink-muted)]">
<span>{post.author}</span>
<span>·</span>
<span>{post.readTime} min read</span>
</div>
</div>
</button>
))}
</div>
</div>
</section>
)
}
56 changes: 56 additions & 0 deletions src/components/BlogPostLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import ShareButtons from './ShareButtons'

export default function BlogPostLayout({ post }) {
if (!post) return null

const formattedDate = new Date(post.date).toLocaleDateString('en-IN', {
year: 'numeric',
month: 'long',
day: 'numeric',
})

return (
<article className="max-w-2xl mx-auto px-6 py-12">
{/* Category */}
<span
className="inline-block text-xs font-bold uppercase tracking-widest px-3 py-1 rounded-full mb-4"
style={{ background: 'var(--purple-light)', color: 'var(--purple)' }}
>
{post.category}
</span>

{/* Title */}
<h1 className="font-display text-3xl md:text-4xl font-bold text-[var(--ink)] leading-tight mb-4">
{post.title}
</h1>

{/* Meta */}
<div className="flex items-center gap-4 text-sm text-[var(--ink-muted)] mb-8">
<span>{post.author}</span>
<span>·</span>
<span>{formattedDate}</span>
<span>·</span>
<span>{post.readTime} min read</span>
</div>

{/* Cover image — pre-sized to prevent CLS */}
<div className="w-full aspect-[16/9] rounded-2xl overflow-hidden mb-8 bg-[var(--cream-dark)]">
<img
src={post.coverImage}
alt={post.title}
className="w-full h-full object-cover"
width={800}
height={450}
/>
</div>

{/* Excerpt */}
<p className="text-lg text-[var(--ink-soft)] leading-relaxed mb-8">
{post.excerpt}
</p>

{/* Share buttons */}
<ShareButtons title={post.title} />
</article>
)
}
75 changes: 75 additions & 0 deletions src/components/ShareButtons.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useState } from 'react'

export default function ShareButtons({ title, url }) {
const shareUrl = url || window.location.href
const [copied, setCopied] = useState(false)

const platforms = [
{
name: 'Twitter / X',
icon: '𝕏',
href: `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(shareUrl)}`,
},
{
name: 'LinkedIn',
icon: 'in',
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`,
},
{
name: 'WhatsApp',
icon: '💬',
href: `https://wa.me/?text=${encodeURIComponent(title + ' ' + shareUrl)}`,
},
]

function handleNativeShare() {
if (navigator.share) {
navigator.share({ title, url: shareUrl })
}
}

function handleCopy() {
navigator.clipboard.writeText(shareUrl).then(() => {
setCopied(true)
setTimeout(() => setCopied(false), 2000)
})
}

return (
<div className="flex flex-wrap items-center gap-3 mt-8">
<span className="text-sm font-semibold text-[var(--ink-muted)] uppercase tracking-wide">
Share
</span>

{typeof navigator !== 'undefined' && navigator.share && (
<button
onClick={handleNativeShare}
className="flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium border border-[var(--border)] hover:border-[var(--purple)] hover:text-[var(--purple)] transition-all"
>
↑ Share
</button>
)}

{platforms.map((p) => (
<a
key={p.name}
href={p.href}
target="_blank"
rel="noopener noreferrer"
aria-label={`Share on ${p.name}`}
className="flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium border border-[var(--border)] hover:border-[var(--purple)] hover:text-[var(--purple)] transition-all"
>
<span>{p.icon}</span>
<span>{p.name}</span>
</a>
))}

<button
onClick={handleCopy}
className="flex items-center gap-2 px-4 py-2 rounded-full text-sm font-medium border border-[var(--border)] hover:border-[var(--purple)] hover:text-[var(--purple)] transition-all"
>
{copied ? '✓ Copied!' : '🔗 Copy Link'}
</button>
</div>
)
}
52 changes: 52 additions & 0 deletions src/data/blog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Blog posts data
*
* To add a blog post, copy the object below and fill in the details.
* Submit a PR with your changes!
*
* Fields:
* id - Unique number
* slug - URL-friendly identifier (e.g. "getting-started-with-ml")
* title - Article title
* excerpt - One or two sentence summary
* date - Publication date (YYYY-MM-DD)
* readTime - Estimated read time in minutes
* category - One of: "AI Basics", "Career", "Tools", "Community"
* coverImage - URL or local path to cover image
* author - Author name
*/
export const blogPosts = [
{
id: 1,
slug: 'getting-started-with-ml',
title: 'Getting Started with Machine Learning as a Student',
excerpt: 'You don\'t need a PhD to start learning ML. Here\'s a practical roadmap for school and college students in India.',
date: '2025-04-10',
readTime: 5,
category: 'AI Basics',
coverImage: 'https://images.unsplash.com/photo-1620712943543-bcc4688e7485?w=800&q=80',
author: 'Ananya Pillai',
},
{
id: 2,
slug: 'women-in-ai-india',
title: 'Women in AI: Closing the Gender Gap in India',
excerpt: 'A look at the current state of gender diversity in Indian AI research and what you can do to be part of the change.',
date: '2025-04-22',
readTime: 7,
category: 'Career',
coverImage: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=800&q=80',
author: 'Riya Sharma',
},
{
id: 3,
slug: 'free-ai-tools-for-students',
title: '7 Free AI Tools Every Student Should Know About',
excerpt: 'From Colab to Hugging Face — here are the tools that will take you from zero to building real AI projects.',
date: '2025-05-01',
readTime: 4,
category: 'Tools',
coverImage: 'https://images.unsplash.com/photo-1555255707-c07966088b7b?w=800&q=80',
author: 'Neha Krishnan',
},
]