How I Built This Portfolio: Inspiration, Systems, and Behind the Code

From discovering SpEcHiDe's terminal portfolio to asking Lovable for a UI concept, building my own Markdown compiler, writing a terminal CLI in Vanilla JS, building the AI Reading Assistant overlay, implementing automated CI/CD workflows, and why my repository is currently private. This is the complete A-to-Z technical blueprint.

Until now, every article on this blog has been about my journey—my old username, lost repositories, hackathons, and reflections on turning 17. It's time for something different: the origin story, full architecture, automated workflows, and a raw A-to-Z technical blueprint of every system running behind this portfolio.


If you look around developer portfolios today, almost all of them follow a familiar template:

  • Next.js or Vite starter kit.
  • TailwindCSS utility classes crammed into every HTML element.
  • Dozens of NPM packages for simple scroll animations.
  • A 15MB JavaScript bundle for a page that displays five project links.

When I started building mkishore.is-a.dev, I wanted something completely different. I wanted a site that felt like a high-performance terminal app wrapped in sleek glassmorphism—sub-50ms load times, zero framework overhead, total control over every line of code, and an automated build system that makes publishing effortless.

Here is the complete story, architectural breakdown, workflow manual, and code tour of everything built into this portfolio.


1. The Spark: A Random Chat & SpEcHiDe

If we rewind to when I first started my programming journey back in 2021, my world revolved around Telegram bots and developer communities.

Back then, there was a well-known developer from Kerala named SpEcHiDe (@SpEcHiDe). He had over 1,000 followers on GitHub and was famous for building Telegram bot frameworks and userbot modules. Like many young developers starting out back then, I looked up to his work. In fact, if you search through my archived repositories, you'll still find MKishoreDev/NoPmBot—a bot repository derived from his original code.

Fast forward to a few months ago.

I was in a casual chat with fellow developers, reminiscing about the old Telegram bot days. During the conversation, someone casually mentioned that SpEcHiDe was active on LinkedIn.

Curious, I searched for his profile and found a link to his personal portfolio: shrimadhavuk.me.

His site was a minimal, ultra-clean terminal UI. It was simple, fast, and iconic.

Seeing it hit me immediately:

"I've been making excuses to delay building my portfolio for months. It's time to build one. And it needs to be great."


2. The First Draft: Lovable & The Original Rule

After months of procrastination, that moment finally killed my excuses.

I opened Lovable and requested an initial UI concept layout to brainstorm how a glassmorphic terminal interface could look. That gave me the visual spark I needed.

When I sat down to write the first line of code, I set one strict rule for myself:

The Original Rule: The portfolio MUST have an interactive terminal UI. No blog, no guestbook, no discussions, no extra pages—just a single, clean portfolio page.

That was how it started. Obviously... I didn't stick to a single page. Step by step, the project expanded into what you're seeing today.

Maximalism vs. Developer Character

While researching ideas for this site, I explored dozens of incredible developer portfolios across GitHub showcase repositories (and eventually submitted my own portfolio to curated GitHub portfolio lists!).

During that search, I saw every possible aesthetic imaginable. My absolute favorite design style to look at is Maximalism—heavy visual effects, rich interactive widgets, dense typography, and constant motion. But as much as I admire maximalist portfolios, I realized something important about myself:

"Maximalism doesn't match my developer character. I am not a UI designer; I am a backend and automation engineer."

I love clean terminal interfaces, raw performance, low latency, and efficient code. So I chose a design that reflected who I actually am: simple, clean, fast, and terminal-driven.

Even now, I still update the codebase almost daily. I tune CSS variables, optimize build scripts, fix minor layout bugs, and add small interactions. Do most visitors notice these tiny daily updates? Probably not. But I build them anyway—because I love the craft of building software with care.


3. The Full Project Structure

Here is the complete directory layout of the portfolio:

📂Portfolio/
📂.github/
📂workflows/
📄blog-auto-build.yml — CI/CD GitHub Actions pipeline
📄CODE_OF_CONDUCT.md
📄PULL_REQUEST_TEMPLATE.md
📂.well-known/
📄llms.txt — Standard-path AI discovery file
📄security.txt — Standard-path security contact
📂assets/
📂css/
📄style.css — Full design system (128KB source)
📄style.min.css — Minified production CSS (104KB)
📄giscus-dark.css — Custom Giscus dark theme
📄giscus-light.css — Custom Giscus light theme
📂js/
📄data.js — Centralized portfolio state (profile, projects, skills)
📄app.js — Main page engine (54KB, 1300+ lines)
📄terminal.js — Interactive CLI terminal (61KB, 1700+ lines)
📄blog.js — Blog rendering engine (26KB, 714 lines)
📄contributions.js — GitHub contribution graph (17KB, 482 lines)
📄guestbook.js — Giscus theme sync engine
📂templates/
📄blog-template.html — Post compilation template
📄blogs-index-template.html — Blog listing template
📄blog-index-template.html — Blog index redirect template
📂blog/
📄*.html — Compiled static HTML blog posts
📂blogs/
📄index.json — Blog posts JSON index
📂posts/
📄*.md — Markdown source files for all blog posts
📂scripts/
📄build.js — Full-stack build & compilation pipeline (1157 lines)
📄auto-update-dates.js — Git-based automatic date tracking
📄404.html — Custom "Page Not Found" page
📄CNAME — Custom domain configuration (mkishore.is-a.dev)
📄feed.xml — RSS 2.0 feed for blog subscribers
📄guestbook.html — Guestbook powered by Giscus (GitHub Discussions)
📄humans.txt — humanstxt.org standard credits file
📄index.html — Main portfolio page (minified, 73KB)
📄llms-full.txt — Extended LLM profile with all project data
📄llms.txt — AI/LLM discovery spec (llmstxt.org)
📄manifest.webmanifest — PWA manifest (installable web app)
📄package.json — Project dependencies & npm scripts
📄projects.json — Machine-readable project index (17 projects)
📄robots.txt — Crawler directives + sitemap references
📄security.txt — Security contact metadata (RFC 9116)
📄sitemap.xml — Master sitemap index
📄sitemap-blog.xml — Blog post sitemap
📄sitemap-images.xml — Image sitemap for Google Images
📄sitemap-pages.xml — Page-level sitemap
📄vercel.json — Deployment config: clean URLs, security headers, caching

4. Centralized Data Architecture (assets/js/data.js)

To ensure both the web interface and the terminal CLI stay 100% in sync without duplicating data, I centralized the entire portfolio state inside assets/js/data.js.

javascript
const SITE_DATA = {
  profile: {
    name: "Kishore M",
    handle: "MKishoreDev",
    tagline: "Backend, API & Automation Engineer",
    location: "Tamil Nadu, India",
    bio: "Building high-performance APIs, automation tools, and minimal web applications."
  },
  projects: [
    {
      name: "Portfolio & Blog Engine",
      tags: ["Node.js", "Vanilla JS", "CSS3", "Markdown"],
      featured: true,
      github: "https://github.com/MKishoreDev/Portfolio",
      live: "https://mkishore.is-a.dev"
    }
    // ...17 projects total
  ],
  skills: {
    backend: ["Node.js", "Express", "Python", "REST APIs"],
    frontend: ["HTML5", "CSS3 (Vanilla)", "JavaScript (ES6+)", "Terminal UI"],
    tools: ["Git", "GitHub Actions", "VS Code", "Vercel"]
  }
};

Both app.js (which renders the web cards) and terminal.js (which handles CLI commands) consume this exact same data object. If I add a new project or skill in data.js, it instantly reflects across the UI and the terminal.


5. The Main Page Engine (assets/js/app.js — 1300+ Lines)

The main index.html page is powered by app.js, which handles a surprising number of interactive features:

Dynamic Age Calculation & Birthday System

The site dynamically calculates my age from my DOB (2009-08-05) and injects it everywhere via window.KISHORE_AGE. But the real magic happens on August 5th: the page detects my birthday and triggers a full celebration mode—a birthday banner appears, a floating 🎂 emoji hat renders above my avatar, and an HTML5 Canvas geometric confetti engine (80 particles, circles and rectangles, fading out smoothly between 12–16 seconds) fires across the screen. Within 7 days before my birthday, a live countdown timer appears with digit boxes (Days:Hours:Mins:Secs) that auto-refreshes when it hits zero.

Spotlight Cursor-Follow Effect

A radial gradient spotlight follows your mouse cursor across the page, creating a dynamic lighting effect on cards and sections:

javascript
var el = document.getElementById('spotlight');
document.addEventListener('mousemove', function(e) {
  el.style.background = 'radial-gradient(circle at ' + e.clientX + 'px ' + e.clientY + 'px, ...)';
});

Dynamic Blinking Terminal Favicon

The browser tab favicon isn't static—it's a dynamically generated canvas favicon that blinks like a real terminal cursor. When the page is visible, a green blinking cursor animates in the favicon. When you switch tabs, it freezes to save resources:

javascript
var canvas = document.createElement('canvas');
canvas.width = 32; canvas.height = 32;
var ctx = canvas.getContext('2d');
// Draw terminal prompt with blinking cursor
setInterval(function() {
  cursorVisible = !cursorVisible;
  // render canvas and set link rel="icon"
}, 500);

Lazy-Loaded Project Screenshots

Project preview images use IntersectionObserver for lazy loading. Images only load when the user scrolls them into view, keeping the initial page weight minimal:

javascript
function initProjectLazyLoad() {
  var lazyImages = document.querySelectorAll('.lazy-project-img');
  var observer = new IntersectionObserver(function(entries) {
    entries.forEach(function(entry) {
      if (entry.isIntersecting) {
        var img = entry.target;
        img.src = img.dataset.src; // Load actual image
        observer.unobserve(img);
      }
    });
  });
  lazyImages.forEach(function(img) { observer.observe(img); });
}

Lazy-Loaded GitHub Contribution Graph

The interactive GitHub contribution graph (contributions.js) is only loaded when the user scrolls to that section, keeping the initial page render instant:

javascript
var ghChart = document.querySelector('.lazy-ghchart');
var observer = new IntersectionObserver(function(entries) {
  if (entries[0].isIntersecting) {
    // Dynamically inject contributions.js script
    var script = document.createElement('script');
    script.src = '/assets/js/contributions.min.js';
    document.body.appendChild(script);
    observer.disconnect();
  }
});
observer.observe(ghChart);

Scroll-Triggered Section Reveal Animations

Every section on the page uses IntersectionObserver-powered reveal animations. As you scroll, sections fade in with smooth CSS transitions:

javascript
var revealObserver = new IntersectionObserver(function(entries) {
  entries.forEach(function(e) {
    if (e.isIntersecting) {
      e.target.classList.add('visible');
      revealObserver.unobserve(e.target);
    }
  });
}, { threshold: 0, rootMargin: '0px 0px -5% 0px' });

Auto-Generated Table of Contents

For blog posts, a floating Table of Contents sidebar is automatically generated from heading tags (h2, h3) with IntersectionObserver-based active heading tracking as the user scrolls.

Dynamic Blog Section Rendering

The blog section on the homepage dynamically renders the latest posts from window.BLOG_POSTS, with automatic section numbering that adjusts when sections are empty.

Live Third-Party API Stats

The stats section fetches real-time data from external APIs—but only when you scroll to it (lazy-loaded via IntersectionObserver):

  • LeetCode Stats: Total problems solved and global ranking via alfa-leetcode-api.onrender.com.
  • MonkeyType Stats: 60-second WPM speed and accuracy via api.monkeytype.com.

Availability Calendar Grid

An interactive weekly schedule matrix (Mon–Sun × Morning/Afternoon/Evening) renders busy, limited, or free status cells so visitors know when I'm available.

Formspree Contact Form

The contact form submits directly to Formspree via JSON fetch with interactive button states (Sending... → Sent! → Error fallback to mailto:).

Konami Code Easter Egg

Try pressing B A on the main page. It triggers Matrix Mode—rendering a full-screen Matrix digital rain animation and launching a canvas-confetti burst!

Keyboard Shortcuts Modal

Pressing ? on any non-input element opens a shortcuts guide modal listing all available keyboard shortcuts.


6. The Interactive GitHub Contribution Graph (assets/js/contributions.js)

This isn't just an image embed—it's a fully interactive, custom-built GitHub contribution graph:

  • Fetches real contribution data via CORS-friendly GitHub API.
  • Renders an interactive grid matching GitHub's exact layout (weekday labels, month columns).
  • Hover tooltips show contribution counts per day.
  • Click modals display detailed contribution info for any date.
  • Year navigation lets you browse contributions from 2021 onwards.
  • Live streak counter and highest contribution day stats.
  • Hides future days dynamically based on the current date.
  • Mobile-responsive with a dropdown year selector and stats grid.

7. The In-Browser Terminal Engine (assets/js/terminal.js — 1700+ Lines)

Clicking the terminal icon (or pressing ~) opens an interactive shell right in the browser. It isn't a fake animation—it's a real command-line event loop.

All 41 Terminal Commands

CommandDescription
helpInteractive ASCII command directory
whoamiProfile specs (age, location, school, CS focus)
aboutDeveloper journey timeline (2021–2026)
skills [python\|js\|db]Technology breakdown with sub-argument filtering
projects [--all] [--lang=<lang>]Formatted project table with filters
project <name>Deep-dive into a specific project's stack and links
statusReal-time availability and timezone
contactSocial links (GitHub, LinkedIn, Telegram, Email)
socialAll social profile links
dateCurrent IST time
uptime"up 4+ years, still compiling"
neofetchCustom Arch Linux ASCII logo + system info
echo <text>Echoes input text
githubLive API fetch from api.github.com
leetcodeLive API fetch for LeetCode stats
monkeytypeLive API fetch for typing speed
weatherLive weather for Tirunelveli from wttr.in
blog listASCII table of all compiled blog posts
blog read <slug>Fetches and renders full blog post inside terminal
guestbook / signRedirects to guestbook page
historyDisplays command history
calc <expr>Safe math expression evaluator
jokeRandom programming joke from API
adviceRandom advice from api.adviceslip.com
catRandom cat fact from catfact.ninja
spaceNASA Astronomy Picture of the Day
ipYour public IP address
qr <text>Generates QR code link
quote / fortuneRandom programming quote
themeToggles site dark/light theme
who"kishore pts/0 (still compiling)"
cowsay <msg>ASCII cow with speech bubble
slSteam locomotive ASCII train animation
birthdayBirthday status or days remaining countdown
ping [kishore]Simulates ICMP ping with random latency
curl [url]Simulates HTTP response with ASCII developer card
git logFormatted fake git commit history
secret / secretsDirectory of secret commands
npm install kishoreAnimated progress bar package installation
sudo <command>See Easter Eggs section below
matrixSee Easter Eggs section below
clearClears terminal output buffer

Core Engine Features:

  • Command History Stack: Up Arrow and Down Arrow navigate through previous commands.
  • Real-Time Ghost Text Autocomplete: As you type, a greyed-out suggestion appears ahead of your cursor (like GitHub Copilot). Press Right Arrow or Tab to accept it.
  • Typewriter Boot Sequence: When the terminal opens, a time-based greeting ("Good morning", "Late night coding?") types out character-by-character at 12ms per char. After the boot sequence completes, the page automatically smooth-scrolls to the terminal input so users immediately know it's interactive and they can start typing.
  • Fuzzy "Did You Mean?" Suggestions: If you type an unrecognized command (e.g. hel, projetcs, neofecth), the terminal uses a Levenshtein distance algorithm to find the closest matching command and suggests it — just like a real shell:
kishore@dev: ~
kishore@dev hel
zsh: command not found: hel
Did you mean: help?
Type "help" for available commands.

The fuzzy matcher uses two strategies: prefix matching for short inputs (e.g. skskills) and edit distance for typos (max 3 edits). This makes the terminal feel polished and forgiving — users are guided to the right command instead of hitting a wall.

  • Terminal Glitch Shake: Error commands trigger a physical CSS shake animation on the terminal window.
  • Smart Mobile Scroll Lock: Uses visualViewport listener to prevent page jumps when the mobile virtual keyboard appears or dismisses. The lock automatically cancels on any user scroll gesture (touchmove or wheel), so the page is never frozen — you can always scroll away from the terminal naturally. The lock duration is a tight 400ms (just enough for keyboard dismiss animation).
  • Touch-Aware Click-to-Focus: On mobile, tapping the terminal focuses the input, but swiping to scroll does not. The system tracks touchstart/touchmove to distinguish taps from scroll gestures, so the virtual keyboard only opens on intentional taps.

7b. Terminal Easter Eggs 🥚

CAUTION
Spoiler Alert: These are hidden features. Try them yourself before reading!

sudo rm -rf / — Fake Kernel Panic

Running any sudo command triggers CRT monitor mode. The terminal overlay goes full-screen, scanlines appear, and fake Linux kernel panic logs stream line-by-line. Then a Red Sudo 404 / Unauthorized Access overlay appears with a progress bar cycling through stages ("Locating IP address...", "Alerting Kishore..."), ending with a dramatic self-destruct sequence. Click anywhere to escape.

matrix — Canvas Digital Rain

Enters a full-screen HTML5 Canvas animation rendering Matrix-style digital rain with Japanese Katakana characters (0x30A0), random white glowing leader characters, and smooth fading trails. Click anywhere to exit.

npm install kishore — Fake Package Install

Simulates a real npm install with an animated ASCII progress bar ([#### ] 40%), package resolution logs, and dependency linking output.

cowsay <message> — ASCII Cow

Generates a classic ASCII cow with a speech bubble containing your custom message.

sl — Steam Locomotive

A full ASCII steam locomotive train animation runs across your terminal screen.


8. The Blog Engine (assets/js/blog.js — 714 Lines)

The blog pages (/blog/* and /blogs/*) are powered by blog.js, which handles:

  1. Reading Progress Bar: A horizontal progress bar at the top of every blog post that fills as you scroll through the article.
  2. Back-to-Top Button: Appears when you scroll past the fold; smoothly scrolls back to the top.
  3. Image Lightbox: Clicking any blog image opens a full-screen lightbox overlay with zoom capability.
  4. Copy Code Button: Every fenced code block has a "Copy" button that copies the code to clipboard with visual feedback.
  5. Share Link Handler: One-click sharing to Twitter, LinkedIn, and clipboard with encoded post metadata.
  6. Blog Listing Search & Filter: The /blogs listing page supports real-time search filtering across post titles, summaries, and tags.
  7. Spotlight Cursor-Follow: The same radial gradient mouse-follow effect from the main page.
  8. Interactive File Tree Explorer: :::filetree containers have collapsible folder toggles that you can click to expand/collapse.
  9. Tab Switching Logic: :::tabs containers have click-to-switch tab panels with state memory.
  10. Dynamic Blinking Favicon: The terminal cursor favicon animates on blog pages too.
  11. Scroll Reveal Animations: Blog content sections fade in with IntersectionObserver.
  12. Mobile Hamburger Menu: Responsive navigation with animated open/close toggle.

9. The Guestbook & Giscus Integration

Instead of building a custom comments backend, I integrated Giscus (GitHub Discussions as comments):

javascript
// Custom Giscus dark/light theme sync
function sendGiscusTheme() {
  var iframe = document.querySelector('iframe.giscus-frame');
  var isDark = document.documentElement.classList.contains('dark');
  var themeUrl = isDark
    ? 'https://mkishore.is-a.dev/assets/css/giscus-dark.css'
    : 'https://mkishore.is-a.dev/assets/css/giscus-light.css';
  iframe.contentWindow.postMessage(
    { giscus: { setConfig: { theme: themeUrl } } },
    'https://giscus.app'
  );
}

I wrote custom CSS theme files (giscus-dark.css and giscus-light.css) so the Giscus iframe perfectly matches the portfolio's glassmorphism aesthetic. A MutationObserver watches for dark/light mode toggles and instantly syncs the Giscus theme via postMessage.

When I looked at SpEcHiDe's blog, I noticed he used Telegram Auth for post comments. While that works great for Telegram communities, I chose Giscus instead. Why? Because developers already carry GitHub credentials, comments remain archived in GitHub repositories, and it eliminates the need to maintain bot webhook polling servers just for blog comments.


10. The Custom Markdown Compiler (scripts/build.js — 1157 Lines)

Instead of relying on Jekyll or Hugo, I built my own Node.js Markdown parser.

Inline Formatting Rules

javascript
// Custom Glitch Text: [glitch:Text]
str.replace(/\[glitch:(.+?)\]/g, '<span class="glitch-text" data-text="$1">$1</span>');

// Custom Neon Text: [neon:Text]
str.replace(/\[neon:(.+?)\]/g, '<span class="neon-text">$1</span>');

// Keyboard Badges: [[Ctrl]] -> <kbd>Ctrl</kbd>
str.replace(/\[\[([^\]]+)\]\]/g, '<kbd>$1</kbd>');

// Inline Mark Highlight: ==Highlight==
str.replace(/==(.+?)==/g, '<mark>$1</mark>');

// Click-to-Reveal Spoiler: ||Spoiler||
str.replace(/\|\|(.+?)\|\|/g, '<span class="spoiler">$1</span>');

// Automatic GitHub Repo Badge Conversion
// Detects raw github.com URLs and converts them into styled SVG repo badges

Custom Container Blocks (:::)

Tabbed panels with click-to-switch logic.

Interactive collapsible directory tree explorers.

Numbered step-by-step timelines.

Responsive multi-column layout grids.

:::

GFM Alert Boxes

NOTE
These callout boxes are parsed from > [!NOTE], > [!TIP], > [!IMPORTANT], > [!WARNING], and > [!CAUTION] markers.

Schema.org JSON-LD Generator

Every compiled blog post automatically embeds structured data:

javascript
function generateJSONLD(post) {
  return JSON.stringify({
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "headline": post.title,
    "author": { "@type": "Person", "name": "Kishore M" },
    "datePublished": post.date,
    "dateModified": post.updated || post.date,
    "description": post.summary
  });
}

Zero-Dependency Build-Time Multi-Language Syntax Highlighter

Instead of bundling heavy client-side highlighters (which cause layout shift and extra browser JS execution overhead), scripts/build.js features a custom zero-dependency build-time code highlighter:

  • Placeholder Tokenization System: Uses non-word control character placeholders (tok_N) to extract strings, comments, object properties, constants, keywords, booleans, and numbers without regex collisions on HTML class="..." attributes.
  • Order-Preserving Extractor: Extracts multi-line strings and single-line strings before comment matching—preventing URLs inside strings (https://schema.org) from being misparsed as comments.
  • JS Object Property & Constant Support: Tokenizes JS object keys (profile:, name:) into <span class="token property"> and ALL_CAPS constants (SITE_DATA) into <span class="token constant">.
  • Glassmorphic Terminal Code Cards: Styled with translucent backdrop filters (backdrop-filter: blur(12px)), rounded corners, violet borders matching --primary, and curated HSL colors for all syntax tokens in external style.css.

11. The CSS Design System (assets/css/style.css — 128KB)

The entire visual design runs on Vanilla CSS with no preprocessors:

  • CSS Custom Properties: 50+ design tokens (--accent, --bg-glass, --text-primary, etc.) for instant theme switching.
  • Glassmorphism: backdrop-filter: blur(12px) with semi-transparent backgrounds on cards, navigation, and terminal overlays.
  • @keyframes Animations: Glitch text effect, neon glow pulse, terminal cursor blink, skeleton loading shimmer, fade-in reveals, and spotlight gradient transitions.
  • Responsive Breakpoints: Mobile-first design with fluid typography using clamp() and container-aware layouts.
  • Dark/Light Mode: Full theming via CSS variables toggled by a single .dark class on <html>.

12. The AI Reading Assistant ("Ask AI") & Reading UX Refinements

As I wrote more articles for this blog, I noticed a pattern: my technical posts were becoming quite detailed and long (often 3,000 to 5,000+ words).

The Evolution: From Ollama to One-Click AI Model Redirection

At first, I brainstormed embedding a custom "Chat with Blog" AI widget directly onto the site using Ollama or local browser LLM runtimes. But while exploring open-source developer portfolio repositories and analyzing performance trade-offs, I hit a key realization:

"Why force visitors to wait for a heavy client-side AI model to download or rely on a rate-limited custom backend, when almost every developer already has an account and active history on their favorite AI platform?"

Instead of over-engineering an embedded chatbot, I built the AI Reading Assistant overlay ("Ask AI"):

  • Location: Integrated directly into the article share row alongside Twitter/X, LinkedIn, WhatsApp, Telegram, and Copy Link.
  • Supported AI Platforms: A single click on Ask AI opens a clean overlay where readers can pick their preferred AI model:
    • 🟢 ChatGPT (OpenAI)
    • Google Gemini
    • 🟧 Anthropic Claude
    • 𝕏 xAI Grok
  • One-Click Discussion: Clicking any model automatically opens that platform in a new tab pre-loaded with a prompt and the blog post link:
text
Read this blog: "[Title]" ([URL]). Let's talk about it!

If readers prefer a different AI model or custom workflow, they can simply copy the blog URL or use the Copy Link button to paste it directly into their favorite AI assistant.

Reading Experience Polish

Along with the AI Reading Assistant, I overhauled several key blog UX components:

  • Bulletproof Inline SVGs: Switched all share icons to clean inline SVGs (including a crisp borderless LinkedIn glyph), ensuring icons render perfectly even under strict privacy extensions or adblockers.
  • Right-Side Section Reading Track: Enhanced the floating section indicator with IntersectionObserver scroll tracking, top/bottom gradient fade blur masks, and automatic hiding on posts with fewer than 3 sections.
  • Lazy-Loaded Giscus Comments: Deferred GitHub Discussions Giscus iframe initialization using IntersectionObserver until the reader approaches the bottom of the page.

13. GitHub Actions CI/CD Workflow

Every push to main that touches blog content, assets, or build scripts triggers an automated GitHub Actions CI/CD pipeline:

yaml
name: 🚀 Blog Auto-Build CI/CD
on:
  push:
    branches: [main]
    paths: ["blogs/**", "assets/**", "scripts/**", "index.html", "404.html"]
  workflow_dispatch:
    inputs:
      force_rebuild:
        description: "Force a full rebuild regardless of changes"
  1. Checkout Repository: Full git history (fetch-depth: 0) for accurate date tracking.
  2. Setup Node.js 22: With npm cache for fast installs.
  3. Validate Blog Frontmatter: Runs scripts/validate-blogs.js to catch missing titles, dates, or tags.
  4. Update Modification Dates: Runs scripts/auto-update-dates.js — compares each post's body against its previous Git version. Only updates the updated: frontmatter field if the content actually changed.
  5. Build Production Assets: npm run build triggers the full minification + compilation pipeline.
  6. Cache Busting: Runs scripts/cache-buster.js to inject fresh version hashes.
  7. Sanity Check: Verifies all required build artifacts exist (index.html, feed.xml, sitemap.xml, projects.json, llms.txt, humans.txt).
  8. Smart Commit & Push: Only commits when there are actual content changes—prevents empty "rebuild" commits from cluttering history. Uses github-actions[bot] as the committer.

The workflow has concurrency control (cancel-in-progress: true) and bot-loop prevention (if: github.actor != 'github-actions[bot]').


14. Vercel Deployment & Security Headers

The site is deployed on Vercel with a custom vercel.json configuration:

json
{
  "cleanUrls": true,
  "headers": [
    {
      "source": "/assets/(css|js)/(.*)\\.min\\.(css|js)",
      "headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
    },
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "Content-Security-Policy", "value": "default-src 'self' 'unsafe-inline' https: data:; object-src 'none';" },
        { "key": "Permissions-Policy", "value": "geolocation=(), camera=(), microphone=(), payment=()" }
      ]
    }
  ]
}
  • Clean URLs: /blog/my-post instead of /blog/my-post.html.
  • Immutable Caching: Minified assets cached for 1 year with cache-busting version hashes.
  • Security Headers: CSP, X-Frame-Options DENY, nosniff, strict referrer policy, and locked-down Permissions-Policy.

15. Web Standards & Discovery Files

Beyond the visible website, there's an entire layer of machine-readable discovery files:

FileStandardPurpose
robots.txtrobotstxt.orgCrawler directives + 4 sitemap references
sitemap.xmlSitemap ProtocolMaster sitemap index linking sub-sitemaps
sitemap-pages.xmlSitemap ProtocolPage-level URLs with lastmod dates
sitemap-blog.xmlSitemap ProtocolBlog post URLs with publication dates
sitemap-images.xmlSitemap ProtocolImage URLs for Google Images indexing
feed.xmlRSS 2.0Blog subscription feed
humans.txthumanstxt.orgHuman-readable credits: author, tools, standards
security.txtRFC 9116Security vulnerability reporting contact
llms.txtllmstxt.orgAI/LLM discovery with bio, projects, feeds
llms-full.txtllmstxt.orgExtended profile with all 17 projects
manifest.webmanifestW3C Web App ManifestPWA installability (standalone display mode)
projects.jsonCustom JSONMachine-readable project index (17 projects)
blogs/index.jsonCustom JSONMachine-readable blog post index

16. What Building This Portfolio Taught Me

1. Google Search Console & Meta Infrastructure

Building this site taught me how Google Search Console crawls, indexes, and ranks URLs. I learned why structured metadata (OpenGraph tags, meta descriptions, canonical URLs, and Schema.org JSON-LD graphs) is the difference between a site that gets indexed in hours versus one that stays invisible.

2. Building a DB-Less Static Blog Architecture

I learned how to construct a 100% database-less blog system. Markdown files, JSON indexes, and static HTML compilation = zero hosting costs, instant load times, and complete immunity to database downtime.

3. SEO Performance & Web Vitals

By enforcing zero layout shifts (CLS), sub-50ms interaction to next paint (INP), and asset minification, the site scores a perfect 100/100 on Google Lighthouse.

4. Free Developer Subdomains & DNS Infrastructure

I discovered the world of free developer subdomains (like is-a.devmkishore.is-a.dev). I learned how CNAME records, A records, DNS propagation, custom domain verification, and automated SSL/TLS certificates work under the hood.

5. Why XML & Plain Text Files Help AI Crawlers & Bots

I learned why structured XML files (sitemap.xml, feed.xml) and plain text standards (llms.txt, llms-full.txt, robots.txt, security.txt, humans.txt) are essential for modern web discovery. Web traffic is no longer just humans in web browsers—AI agents, LLM scrapers, and automated bots actively index developer profiles. Providing clean XML feeds and standard llms.txt files allows AI search engines (like ChatGPT, Claude, and Perplexity) to index my projects, stack, and technical articles accurately without struggling through client-side JavaScript execution loops.


17. Things People Notice vs. Things People Don't

Things People Notice ✨

  • Glassmorphic backdrop filters and smooth dark theme
  • The interactive CLI terminal with real commands
  • AI Reading Assistant overlay ("Ask AI") launching pre-filled prompts for ChatGPT, Gemini, Claude, and Grok
  • Right-Side Vertical Section Reading Indicator with top/bottom fade blur masks
  • Clean typography (Space Grotesk + Inter + JetBrains Mono)
  • Responsive card layouts with hover spotlight effects
  • Reading progress bar on blog posts
  • Image lightbox with full-screen zoom
  • Copy-to-clipboard buttons on code blocks

Things People Don't Notice ⚙️

  • Service Worker (sw.js) pre-caching with custom offline fallback (offline.html) and live reconnect toast
  • 100% Inline vector SVGs immune to privacy shields and adblockers
  • Dynamic blinking terminal cursor as the browser favicon
  • Schema.org JSON-LD structured data on every page
  • Master sitemap index hierarchy (4 sub-sitemaps)
  • Per-page Git-tracked Last Updated: timestamps
  • llms.txt + llms-full.txt for AI/LLM discovery
  • humans.txt + security.txt web standards compliance
  • PWA manifest (site is installable as an app)
  • Content Security Policy and security headers
  • Lazy-loaded Giscus comments & contribution graph via IntersectionObserver
  • Lazy-loaded project screenshots (images load on scroll)
  • Giscus iframe theme sync via postMessage + MutationObserver
  • Bot-loop prevention in CI/CD workflow
  • GitHub Actions concurrency control (cancel-in-progress)

18. How to Build Your Own Zero-Framework Portfolio (Blueprint)

If you're a developer planning to build your own portfolio from scratch, here is the exact 5-step blueprint:

  1. Design System: Create index.html and define CSS variables in style.css for colors, glassmorphic backdrop filters (backdrop-filter: blur(12px)), and responsive fonts (Space Grotesk, Inter, JetBrains Mono via Google Fonts).
  2. Centralize Data: Create data.js containing your profile, project links, skills, and social handles in a single JavaScript object. Both your UI renderer and terminal CLI should read from this same source of truth.
  3. Build the CLI Terminal: Write a JavaScript module (terminal.js) with an input listener for Enter, Up Arrow, Down Arrow, and Tab, mapping command strings to functions that output data from data.js.
  4. Create a Markdown Compiler: Write a simple Node.js script using fs and regex to read Markdown files, wrap them in an HTML template, and output static HTML files. Add cache-busting version hashes (?v=TIMESTAMP) to asset references.
  5. Deploy & Add Free Domain: Push to GitHub, deploy for free on Vercel or GitHub Pages. Register a free is-a.dev subdomain by opening a PR on the is-a-dev/register repository. Add Giscus for comments, robots.txt for crawlers, and sitemap.xml for Google Search Console!

19. Why Is My Portfolio Repository Private?

People occasionally ask why my portfolio repository isn't open-source on GitHub.

Here is the honest truth:

  1. Constant Instability: I am constantly modifying styles, experimenting with build scripts, removing features, and pushing quick experimental commits. The codebase is often in flux and unstable.
  2. Personal Playground: It's my personal scratchpad where I test ideas without worrying about maintaining a clean public repository structure.
  3. Niche Use Case: I feel nobody really needs this specific codebase as an off-the-shelf template since it's tailored closely to my personal setup.
TIP
Want the Source Code or Architecture? If you're interested in how this portfolio is built or want to use parts of the architecture for your own site... let me know! Scroll down and leave a message on the Guestbook or send me a message on LinkedIn. If you need it, I'll gladly share the code, clean it up, or help you adapt it!

Final Thoughts

Building your own portfolio from scratch—instead of relying on templates—teaches you how the web actually works under the hood.

You learn regex parsing, DOM manipulation, CSS layout math, build performance, RSS spec formatting, SEO structured data, security headers, CI/CD automation, and DNS infrastructure.

I still have a long backlog of improvement ideas I brainstormed while building this—like interactive API playgrounds, live terminal WebSocket relays, and custom RSS topic filters. I intentionally held back on implementing all of them at once to keep the initial build focused, fast, and stable instead of over-engineering. Good software isn't built all at once; it evolves over time.

This portfolio is more than just a place to display my projects. It's a living playground where I test ideas, experiment with systems, and refine my engineering skills.

Thank you for reading!


P.S. The build script that generated this exact page took 1.2 seconds to minify assets, parse Markdown, build sitemaps, update RSS feeds, and render static HTML. Zero frameworks required.

Discussions