diff --git a/app/(blog)/blog.module.css b/app/(blog)/blog.module.css
index 82aadad..9964051 100644
--- a/app/(blog)/blog.module.css
+++ b/app/(blog)/blog.module.css
@@ -194,6 +194,64 @@
object-fit: cover;
}
+/* ───────────────────────────────────────────
+ Overlay Hero
+ ─────────────────────────────────────────── */
+
+.overlayHero {
+ position: relative;
+ width: 100vw;
+ margin-left: calc(50% - 50vw);
+ margin-top: -80px;
+ aspect-ratio: 16 / 9;
+ max-height: 480px;
+ overflow: hidden;
+ margin-bottom: 64px;
+}
+
+.overlayImage {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.overlayGradient {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ to bottom,
+ transparent 20%,
+ rgba(11, 11, 11, 0.5) 50%,
+ rgba(11, 11, 11, 0.85) 75%,
+ rgb(11, 11, 11) 100%
+ );
+}
+
+.overlayHeader {
+ position: absolute;
+ bottom: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 100%;
+ max-width: var(--col-max);
+ padding: 0 24px 32px;
+ z-index: 1;
+}
+
+.overlayHeader .postMeta {
+ margin-bottom: 16px;
+ color: rgba(240, 237, 232, 0.7);
+}
+
+.overlayHeader .postTitle {
+ color: #fff;
+ text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
+}
+
+.overlayHeader .postSubtitle {
+ color: rgba(240, 237, 232, 0.8);
+}
+
/* ───────────────────────────────────────────
Prose (MDX content)
─────────────────────────────────────────── */
diff --git a/app/(blog)/blog/[slug]/page.tsx b/app/(blog)/blog/[slug]/page.tsx
index a365f40..f477883 100644
--- a/app/(blog)/blog/[slug]/page.tsx
+++ b/app/(blog)/blog/[slug]/page.tsx
@@ -38,6 +38,39 @@ export default async function BlogPostPage({ params }: PageProps) {
if (!post) notFound()
+ if (post.meta.overlay && post.meta.image) {
+ return (
+
+
+

+
+
+
+
+
+
+
+
+ )
+ }
+
return (
diff --git a/blog/your-basic-blog.md b/blog/your-basic-blog.overlay.md
similarity index 100%
rename from blog/your-basic-blog.md
rename to blog/your-basic-blog.overlay.md
diff --git a/lib/blog/loadBlog.ts b/lib/blog/loadBlog.ts
index 2958232..ab78b3a 100644
--- a/lib/blog/loadBlog.ts
+++ b/lib/blog/loadBlog.ts
@@ -24,7 +24,8 @@ export function getAllPosts(): BlogMeta[] {
const files = fs.readdirSync(blogDirectory).filter((f) => f.endsWith('.md'))
const posts: BlogMeta[] = files.map((fileName) => {
- const slug = fileName.replace(/\.md$/, '')
+ const overlay = fileName.endsWith('.overlay.md')
+ const slug = fileName.replace(/\.overlay\.md$/, '').replace(/\.md$/, '')
const filePath = path.join(blogDirectory, fileName)
const fileContent = fs.readFileSync(filePath, 'utf-8')
const { data } = matter(fileContent)
@@ -35,6 +36,7 @@ export function getAllPosts(): BlogMeta[] {
date: data.date ? String(data.date) : '',
image: findImage(slug),
readingTime: data.readingTime,
+ overlay,
slug,
}
})
@@ -43,7 +45,10 @@ export function getAllPosts(): BlogMeta[] {
}
export function getPostBySlug(slug: string): BlogPost | null {
- const filePath = path.join(blogDirectory, `${slug}.md`)
+ const overlayPath = path.join(blogDirectory, `${slug}.overlay.md`)
+ const regularPath = path.join(blogDirectory, `${slug}.md`)
+ const overlay = fs.existsSync(overlayPath)
+ const filePath = overlay ? overlayPath : regularPath
if (!fs.existsSync(filePath)) return null
const fileContent = fs.readFileSync(filePath, 'utf-8')
@@ -56,6 +61,7 @@ export function getPostBySlug(slug: string): BlogPost | null {
date: data.date ? String(data.date) : '',
image: findImage(slug),
readingTime: data.readingTime,
+ overlay,
slug,
},
content,
diff --git a/lib/blog/types.ts b/lib/blog/types.ts
index d658b79..02bfad3 100644
--- a/lib/blog/types.ts
+++ b/lib/blog/types.ts
@@ -4,6 +4,7 @@ export interface BlogMeta {
date: string
image?: string
readingTime?: string
+ overlay?: boolean
slug: string
}