Skip to content

Commit 3990faf

Browse files
author
CloudLobster
committed
feat(blog): add hero images to SSR blog pages and index
- Hero image in article pages (full-width, rounded) - Hero image in blog index cards with cover aspect ratio - OG/Twitter meta image tags for social sharing - Index cards now have overflow:hidden + content padding
1 parent 5579d8f commit 3990faf

File tree

1 file changed

+19
-6
lines changed

1 file changed

+19
-6
lines changed

web/scripts/build-blog.mjs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function parseFrontmatter(md) {
9494
}
9595

9696
// Blog page template
97-
function blogTemplate(meta, bodyHtml, slug) {
97+
function blogTemplate(meta, bodyHtml, slug, heroImage) {
9898
const title = meta.title || 'Blog Post';
9999
const description = meta.description || title;
100100
const published = meta.published || new Date().toISOString().slice(0, 10);
@@ -119,9 +119,11 @@ function blogTemplate(meta, bodyHtml, slug) {
119119
<meta property="og:site_name" content="BaseMail Blog" />
120120
<meta property="article:published_time" content="${published}" />
121121
122+
${heroImage ? `<meta property="og:image" content="https://basemail.ai${heroImage}" />` : ''}
122123
<meta name="twitter:card" content="summary_large_image" />
123124
<meta name="twitter:title" content="${esc(title)}" />
124125
<meta name="twitter:description" content="${esc(description)}" />
126+
${heroImage ? `<meta name="twitter:image" content="https://basemail.ai${heroImage}" />` : ''}
125127
126128
<script type="application/ld+json">
127129
${JSON.stringify({
@@ -187,6 +189,7 @@ function blogTemplate(meta, bodyHtml, slug) {
187189
</nav>
188190
189191
<article>
192+
${heroImage ? `<img src="${heroImage}" alt="${esc(title)}" style="width:100%;aspect-ratio:3/2;object-fit:cover;border-radius:12px;margin-bottom:24px" />` : ''}
190193
<h1>${esc(title)}</h1>
191194
<div class="meta">${published} · ${meta.author || 'BaseMail Team'}${tags ? ` · ${tags}` : ''}</div>
192195
${bodyHtml}
@@ -230,8 +233,10 @@ function indexTemplate(posts) {
230233
h1 { font-size: 2.5em; margin-bottom: 8px; }
231234
.subtitle { color: var(--muted); margin-bottom: 40px; font-size: 16px; }
232235
233-
.post { display: block; padding: 24px; background: var(--card); border: 1px solid var(--border); border-radius: 12px; margin-bottom: 16px; text-decoration: none; transition: border-color 0.2s; }
236+
.post { display: block; background: var(--card); border: 1px solid var(--border); border-radius: 12px; margin-bottom: 24px; text-decoration: none; transition: border-color 0.2s; overflow: hidden; }
234237
.post:hover { border-color: rgba(0,82,255,0.4); }
238+
.post .hero { width: 100%; aspect-ratio: 3/2; object-fit: cover; display: block; }
239+
.post .content { padding: 24px; }
235240
.post h2 { color: white; font-size: 1.3em; margin-bottom: 8px; }
236241
.post p { color: var(--muted); font-size: 14px; margin-bottom: 8px; }
237242
.post .date { color: #555; font-size: 12px; }
@@ -255,9 +260,12 @@ function indexTemplate(posts) {
255260
256261
${posts.map(p => `
257262
<a href="/blog/${p.slug}" class="post">
258-
<h2>${esc(p.title)}</h2>
259-
<p>${esc(p.description)}</p>
260-
<div class="date">${p.published}${p.tags ? ` · ${p.tags}` : ''}</div>
263+
${p.heroImage ? `<img class="hero" src="${p.heroImage}" alt="${esc(p.title)}" loading="lazy" />` : ''}
264+
<div class="content">
265+
<h2>${esc(p.title)}</h2>
266+
<p>${esc(p.description)}</p>
267+
<div class="date">${p.published}${p.tags ? ` · ${p.tags}` : ''}</div>
268+
</div>
261269
</a>`).join('\n')}
262270
</main>
263271
@@ -285,8 +293,12 @@ function build() {
285293
const { meta, content } = parseFrontmatter(md);
286294
const slug = basename(file, '.md');
287295
const bodyHtml = md2html(content);
296+
297+
// Check if hero image exists
298+
const heroPath = join(import.meta.dirname, '..', 'public', 'blog', `${slug}.webp`);
299+
const heroImage = existsSync(heroPath) ? `/blog/${slug}.webp` : null;
288300

289-
const html = blogTemplate(meta, bodyHtml, slug);
301+
const html = blogTemplate(meta, bodyHtml, slug, heroImage);
290302
const outDir = join(DIST_DIR, slug);
291303
mkdirSync(outDir, { recursive: true });
292304
writeFileSync(join(outDir, 'index.html'), html);
@@ -297,6 +309,7 @@ function build() {
297309
description: meta.description || '',
298310
published: meta.published || '',
299311
tags: meta.tags || '',
312+
heroImage,
300313
});
301314

302315
console.log(`✅ ${slug}/index.html`);

0 commit comments

Comments
 (0)