April 9, 2026
Upcoming / ScheduledFeaturedREadme File
Passionate about bridging engineering with education to empower teams and drive product impact.
Passionate about bridging engineering with education to empower teams and drive product impact.
Blog Implementation
A markdown-powered personal blog has been integrated in this project inspired by https://github.com/machadop1407/nextjs-markdown-blog and upgraded to modern Next.js App Router patterns.
Routes
- /blog: blog listing page
- /blog/[slug]: blog details page
- /post: alias route to the same listing UI and data
- /post/[slug]: alias route to the same details UI and data
Where Content Lives
Create markdown files in:
- src/content/blog
Each file name becomes the slug. Example:
- src/content/blog/my-awesome-post.md -> /blog/my-awesome-post
Markdown Frontmatter
Each markdown file supports:
---
title: "My First Post"
date: "2026-03-31"
excerpt: "A short summary for card previews"
tags:
- nextjs
- markdown
---
# Heading
Your markdown content here.
Architecture
- src/lib/blog.ts: Server-side markdown loader, frontmatter parsing, sorting, and HTML transform
- src/types/blog.ts: Blog domain types
- src/components/blog/BlogList.tsx: Shared listing UI
- src/components/blog/BlogPostView.tsx: Shared article UI
- src/app/blog/page.tsx: Blog index route
- src/app/blog/[slug]/page.tsx: Blog article route with static params + metadata
- src/app/post/page.tsx and src/app/post/[slug]/page.tsx: Route aliases that reuse blog routes
Key Next.js Features Used
- App Router route segments in src/app
- generateStaticParams for pre-rendering blog posts
- generateMetadata for per-post SEO metadata
- Server Components for file-system markdown loading
Local Development
npm install
npm run dev
Then visit:
Adding New Posts
- Add a new .md file under src/content/blog.
- Fill frontmatter and markdown body.
- Restart dev server only if file watchers are stale; usually hot reload picks it up.
- Open /blog to verify ordering and rendering. Passionate about bridging engineering with education to empower teams and drive product impact.
Theme System (Light, Dark, System)
This project now supports 3 theme modes across the website:
lightdarksystem(matches the OS/browser preference)
Theme state is handled with next-themes, persisted in local storage, and applied with a class-based strategy (class="dark") for Tailwind CSS v4.
How It Works
ThemeProviderwraps the app insrc/app/layout.tsx.- Tailwind dark variant is enabled in
src/app/globals.cssusing:
@custom-variant dark (&:where(.dark, .dark *));
- CSS tokens switch by class:
:root {
--background: #ffffff;
--foreground: #171717;
}
.dark {
--background: #0a0a0a;
--foreground: #ededed;
}
ThemeTogglein the header lets users selectlight,dark, orsystemdynamically.
How To Write Theme-Safe Classes (For Future Work)
Use Tailwind utility pairs where needed:
- Backgrounds:
bg-white dark:bg-slate-900 - Text:
text-slate-900 dark:text-slate-100 - Borders:
border-slate-200 dark:border-slate-700 - Secondary text:
text-slate-600 dark:text-slate-300
Examples:
<div className="rounded-xl bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700">
<h2 className="text-slate-900 dark:text-slate-100">Title</h2>
<p className="text-slate-600 dark:text-slate-300">Description</p>
</div>
<button className="bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-400">
Action
</button>
Rules To Keep Theme Support Stable
- Always provide a
dark:counterpart for non-brand neutral colors. - Prefer semantic color families (
slate,neutral) for text/surface in content-heavy areas. - Keep brand CTA colors consistent unless there is a contrast issue.
- For reusable sections with many custom colors (like resume), use CSS variables and override them under
.dark. - Avoid relying only on
prefers-color-scheme; user choice from toggle should take priority.
Files Added/Updated For Theming
src/components/ThemeProvider.tsxsrc/components/ThemeToggle.tsxsrc/app/layout.tsxsrc/app/globals.css- Shared page/component styles updated with
dark:variants - Resume styles converted to variable-based theme tokens in
src/components/resume/Resume.css
References Used
- https://dev.to/khanrabiul/nextjs-tailwindcss-v4-how-to-add-darklight-theme-with-next-themes-3c6l
- https://medium.com/@aashekmahmud/implementing-dark-and-light-mode-themes-in-next-js-a-comprehensive-guide-bf2c34ecd50d
Linting
This project uses the ESLint CLI with a Next.js 16 flat config.
npm run lintruns ESLint across the project.npm run lint:fixapplies safe autofixes.npm run lint:strictfails on any warning.
The config includes:
eslint-config-next/core-web-vitalseslint-config-next/typescript- the recommended rule sets from
eslint-plugin-react,eslint-plugin-react-hooks, and@next/eslint-plugin-next eslint-config-prettier/flatto avoid formatter conflicts
Contact Form Integrations
The contact form on /contact submits inquiries to two destinations:
- Google Sheet via webhook URL (for spreadsheet logging)
- Email delivery to
jain.mrinal140@gmail.com
Preferred email path (recommended when local or cloud network blocks SMTP):
- Resend HTTPS API
Fallback email path:
- SMTP (Gmail or other provider)
Option A: Resend (recommended)
Set these:
RESEND_API_KEY: API key from ResendRESEND_FROM_EMAIL: verified sender in Resend (example:Contact <onboarding@resend.dev>for testing)
When both variables are present, the API uses Resend and skips SMTP.
Option B: SMTP
Set these environment variables in .env.local and production:
CONTACT_SHEET_WEBHOOK_URL: your Google Apps Script (or other webhook) endpoint for writing rowsCONTACT_SMTP_HOST: SMTP host (example:smtp.gmail.com)CONTACT_SMTP_PORT: SMTP port (example:587or465)CONTACT_SMTP_FAMILY: optional IP family,4(default) or6CONTACT_SMTP_USER: SMTP username/loginCONTACT_SMTP_PASS: SMTP password or app passwordCONTACT_FROM_EMAIL: optional from-address shown on outgoing emails (defaults toCONTACT_SMTP_USER)
If either sheet logging or email delivery fails, the API returns an error and the form shows an error message.
IndexNow Setup
This project includes a script to submit URLs from your sitemap to IndexNow.
1) Add environment variables
Copy values from .env.example into your local .env.local (and production env on your host):
NEXT_PUBLIC_SITE_URLINDEXNOW_KEYINDEXNOW_KEY_LOCATION(optional)INDEXNOW_SITEMAP_URL(optional)INDEXNOW_ENDPOINT(optional)
2) Prove site ownership (required by IndexNow)
Create a text file in public/ named exactly {INDEXNOW_KEY}.txt.
The file content must be only your key.
Example:
- file path:
public/f34f184d10c049ef99aa7637cdc4ef04.txt - file content:
f34f184d10c049ef99aa7637cdc4ef04
After deploy, verify:
https://<your-domain>/<INDEXNOW_KEY>.txtreturns the key text.
3) Submit URLs
Submit all URLs from sitemap:
npm run indexnow:submit:all
Submit URLs changed after a date:
npm run indexnow:submit -- --since=2026-01-01
Submit one URL:
npm run indexnow:submit -- --url=https://<your-domain>/post/example
Dry run (prepare URLs only, no API call):
npm run indexnow:submit -- --all --dry-run
Notes:
- IndexNow accepts up to 10,000 URLs per request. The script auto-batches.
- Non-200/202 responses fail the command so you can retry or debug quickly.
4) GitHub Actions automation (optional)
Workflow file:
.github/workflows/indexnow-submit.yml
Add these GitHub Actions repository secrets:
NEXT_PUBLIC_SITE_URL(required)INDEXNOW_KEY(required)INDEXNOW_KEY_LOCATION(optional)INDEXNOW_SITEMAP_URL(optional)INDEXNOW_ENDPOINT(optional)
What it does:
- Runs daily on a schedule (UTC).
- Supports manual runs with
all,since, orurlmode. - Verifies sitemap and key file are reachable before submitting.
Manual run from GitHub UI:
- Open Actions -> IndexNow Submit -> Run workflow.
- Choose
allto submit all sitemap URLs. - Choose
sinceto submit URLs changed after a date (YYYY-MM-DD). - Choose
urlto submit one URL. - Optionally enable
dry_runto validate without calling IndexNow.