Skip to content

Commit f2c3ff0

Browse files
content(blog): rewrite GSoC and Weekend Tech Grind posts, retune image presentation
GSoC post: rewrite as a transparent breakdown of 9 GSoC selections plus 5 selections across LFX, SSoC, ESoC, and GSSoC (14 acceptances across 13 people, with Talha doing GSoC + SSoC). Each contributor has a one-paragraph introduction with org context and project link. The 5 designed acceptance posters (gsoc, gsoc2, gsoc3, gsoc5, gsoc7) are placed alongside the contributors they depict, with captions that name the people in each. Weekend Tech Grind post: rewrite with sensory framing of the room, concrete format details (2 days, 12 hours per day, monthly cadence, goal declaration on Day 1, deep-work blocks, Sunday closing ritual), and a proper "who this is and is not for" section. Cover image swapped to weekend-grind.jpg. Detail page typography: bigger section spacing (h2 mt-20, paragraphs my-6 with leading-1.85), proper styling for h3, ul, ol, li, blockquote, hr, and softer link underlines so long-form prose breathes. Blog list/featured cards: replace hardcoded slug-based cropping with frontmatter-driven imageFit and imagePosition, default portrait posters to aspect-4/5 + object-contain on a near-black backdrop so headshots are not cropped. Numbers throughout written as digits, em-dashes removed.
1 parent e6d8444 commit f2c3ff0

5 files changed

Lines changed: 221 additions & 484 deletions

File tree

app/blog/[slug]/page.tsx

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -56,82 +56,110 @@ export default async function BlogPost({
5656
</header>
5757

5858
{/* COVER IMAGE */}
59-
{/* COVER IMAGE */}
60-
{data.image && (
61-
<div className="relative w-full mb-16 overflow-hidden rounded-2xl bg-black">
62-
<div className="relative aspect-[16/9] w-full">
63-
<Image
64-
src={data.image}
65-
alt={data.title}
66-
fill
67-
priority
68-
sizes="(max-width: 1024px) 100vw, 1200px"
69-
className={`transition duration-700 hover:scale-105 ${
70-
slug === "gsoc"
71-
? "object-contain"
72-
: "object-cover object-center"
73-
}`}
74-
/>
75-
</div>
76-
</div>
77-
)}
59+
{data.image && (
60+
(() => {
61+
const fit: "cover" | "contain" =
62+
data.imageFit ?? (slug === "gsoc" ? "contain" : "cover");
63+
const aspect = fit === "contain" ? "aspect-[4/5]" : "aspect-[16/9]";
64+
const fitClass =
65+
fit === "contain" ? "object-contain" : "object-cover object-center";
66+
return (
67+
<div className="relative w-full mb-16 overflow-hidden rounded-2xl bg-neutral-950">
68+
<div className={`relative ${aspect} w-full mx-auto max-w-3xl`}>
69+
<Image
70+
src={data.image}
71+
alt={data.title}
72+
fill
73+
priority
74+
sizes="(max-width: 1024px) 100vw, 1024px"
75+
className={`transition duration-700 hover:scale-105 ${fitClass}`}
76+
/>
77+
</div>
78+
</div>
79+
);
80+
})()
81+
)}
7882
{/* MARKDOWN */}
7983
<article className="mx-auto max-w-2xl">
80-
<div
81-
className="prose prose-lg prose-neutral max-w-none
82-
prose-headings:font-serif
83-
prose-h2:mt-12
84-
prose-h3:mt-8
85-
prose-p:text-gray-700 prose-p:leading-8
86-
prose-img:rounded-xl prose-img:my-10"
87-
>
84+
<div className="prose prose-lg prose-neutral max-w-none">
8885
<ReactMarkdown
8986
remarkPlugins={[remarkGfm]}
9087
components={{
91-
/* ✅ Prevent <p> wrapping images */
9288
p({ node, children }) {
9389
const hasImg = node?.children?.some(
9490
(child: any) => child.tagName === "img"
9591
);
9692

9793
if (hasImg) {
98-
return <div className="my-6">{children}</div>;
94+
return <div className="my-12">{children}</div>;
9995
}
10096

10197
return (
102-
<p className="text-gray-700 leading-8 my-4">
98+
<p className="text-gray-800 leading-[1.85] my-6 text-[1.075rem]">
10399
{children}
104100
</p>
105101
);
106102
},
107103

108-
/* ✅ Proper markdown images */
109104
img({ src = "", alt = "" }) {
110105
return (
111-
<span className="block my-10">
106+
<span className="block my-14">
112107
<Image
113108
src={src}
114109
alt={alt}
115-
width={1200}
116-
height={600}
117-
className="rounded-xl object-cover w-full h-auto"
110+
width={1600}
111+
height={2000}
112+
sizes="(max-width: 768px) 100vw, 768px"
113+
className="rounded-xl w-full h-auto"
118114
/>
119115
</span>
120116
);
121117
},
122118

123119
h1: ({ children }) => (
124-
<h1 className="text-4xl font-serif mt-10 mb-6">
120+
<h1 className="text-4xl md:text-5xl font-serif tracking-tight mt-16 mb-8 text-black">
125121
{children}
126122
</h1>
127123
),
128124

129125
h2: ({ children }) => (
130-
<h2 className="text-2xl font-serif mt-10 mb-4">
126+
<h2 className="text-3xl font-serif tracking-tight mt-20 mb-6 text-black">
131127
{children}
132128
</h2>
133129
),
134130

131+
h3: ({ children }) => (
132+
<h3 className="text-xl font-serif font-semibold mt-12 mb-3 text-black">
133+
{children}
134+
</h3>
135+
),
136+
137+
ul: ({ children }) => (
138+
<ul className="my-6 space-y-2 list-disc pl-6 text-gray-800 leading-[1.75]">
139+
{children}
140+
</ul>
141+
),
142+
143+
ol: ({ children }) => (
144+
<ol className="my-6 space-y-2 list-decimal pl-6 text-gray-800 leading-[1.75]">
145+
{children}
146+
</ol>
147+
),
148+
149+
li: ({ children }) => (
150+
<li className="pl-1">{children}</li>
151+
),
152+
153+
blockquote: ({ children }) => (
154+
<blockquote className="my-8 border-l-2 border-black pl-6 italic text-gray-700">
155+
{children}
156+
</blockquote>
157+
),
158+
159+
hr: () => (
160+
<hr className="my-16 border-0 h-px bg-gray-200" />
161+
),
162+
135163
strong: ({ children }) => (
136164
<strong className="font-semibold text-black">
137165
{children}
@@ -141,7 +169,7 @@ export default async function BlogPost({
141169
a: ({ href, children }) => (
142170
<a
143171
href={href}
144-
className="underline text-black hover:opacity-70"
172+
className="underline underline-offset-4 decoration-gray-300 text-black hover:decoration-black transition"
145173
>
146174
{children}
147175
</a>

app/blog/blogClient.tsx

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ import "swiper/css";
1313
import "swiper/css/navigation";
1414
import "swiper/css/pagination";
1515

16+
function imageClasses(post: Post) {
17+
const fit = post.imageFit === "contain" ? "object-contain" : "object-cover";
18+
const positionMap = {
19+
top: "object-top",
20+
center: "object-center",
21+
bottom: "object-bottom",
22+
} as const;
23+
const position = positionMap[post.imagePosition ?? "center"];
24+
return `${fit} ${position}`;
25+
}
26+
1627
export default function BlogClient({ posts }: { posts: Post[] }) {
1728
const [activeTab, setActiveTab] = useState<"news" | "blog">("news");
1829

@@ -106,25 +117,22 @@ export default function BlogClient({ posts }: { posts: Post[] }) {
106117
className="block group"
107118
>
108119
{/* IMAGE */}
109-
<div className="relative w-full aspect-[16/9] mb-5 overflow-hidden rounded-xl bg-gray-100">
110-
120+
<div
121+
className={`relative w-full aspect-[4/5] mb-5 overflow-hidden rounded-xl ${
122+
post.imageFit === "contain"
123+
? "bg-neutral-950"
124+
: "bg-gray-100"
125+
}`}
126+
>
111127
<Image
112128
src={post.image || "/post1.jpg"}
113129
alt={post.title}
114130
fill
115131
priority
116132
sizes="(max-width:768px) 100vw, 33vw"
117-
118-
/*
119-
FIX:
120-
gsoc image uses object-top
121-
others remain centered
122-
*/
123-
className={`rounded-xl transition duration-500 group-hover:scale-[1.02] object-cover ${
124-
post.slug === "gsoc"
125-
? "object-top"
126-
: "object-center"
127-
}`}
133+
className={`rounded-xl transition duration-500 group-hover:scale-[1.02] ${imageClasses(
134+
post
135+
)}`}
128136
/>
129137
</div>
130138

@@ -192,17 +200,21 @@ export default function BlogClient({ posts }: { posts: Post[] }) {
192200
className="block group"
193201
>
194202
{/* IMAGE */}
195-
<div className="relative w-full aspect-[16/9] mb-6 overflow-hidden rounded-xl">
203+
<div
204+
className={`relative w-full mb-6 overflow-hidden rounded-xl ${
205+
post.imageFit === "contain"
206+
? "aspect-[4/5] bg-neutral-950"
207+
: "aspect-[16/9] bg-gray-100"
208+
}`}
209+
>
196210
<Image
197211
src={post.image || "/post1.jpg"}
198212
alt={post.title}
199213
fill
200214
sizes="100vw"
201-
className={`rounded-xl transition duration-500 group-hover:scale-[1.02] object-cover ${
202-
post.slug === "gsoc"
203-
? "object-top"
204-
: "object-center"
205-
}`}
215+
className={`rounded-xl transition duration-500 group-hover:scale-[1.02] ${imageClasses(
216+
post
217+
)}`}
206218
/>
207219
</div>
208220

app/blog/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ export type Post = {
1010
category: string;
1111
description?: string;
1212
image?: string;
13+
imageFit?: "cover" | "contain";
14+
imagePosition?: "top" | "center" | "bottom";
1315
featured?: boolean;
1416
order?: number;
1517
};
1618

19+
const PORTRAIT_POSTER_SLUGS = new Set(["gsoc"]);
20+
1721
export default function BlogPage() {
1822
const blogDir = path.join(process.cwd(), "content", "blog");
1923
const files = fs.readdirSync(blogDir);
@@ -22,14 +26,18 @@ export default function BlogPage() {
2226
const filePath = path.join(blogDir, file);
2327
const fileContent = fs.readFileSync(filePath, "utf-8");
2428
const { data } = matter(fileContent);
29+
const slug = file.replace(".md", "");
2530

2631
return {
27-
slug: file.replace(".md", ""),
32+
slug,
2833
title: data.title,
2934
date: data.date,
3035
category: data.category,
3136
description: data.description || "",
3237
image: data.image || "/post1.jpg",
38+
imageFit:
39+
data.imageFit ?? (PORTRAIT_POSTER_SLUGS.has(slug) ? "contain" : "cover"),
40+
imagePosition: data.imagePosition ?? "center",
3341
featured: data.featured || false,
3442
order: data.order ?? 999,
3543
};

0 commit comments

Comments
 (0)