Portfolio Site
editorial portfolio on a two-register design system, shipping almost no JavaScript
Project metadata
Overview
Personal portfolio built as a monochrome Swiss editorial system with two colour registers, paper and plate, and a hard performance budget enforced in CI. Every page is statically generated from an Obsidian vault, so the site and the source notes cannot disagree.
Design
A rigid twelve-column grid, structure from 1px rules rather than cards, poster-scale neo-grotesque type at tight negative tracking against large negative space, and a single functional accent held under ten percent of any viewport.
Two registers rather than a light and dark mode: the same document printed on paper stock or as a photographic plate. Layout, type, spacing, and rule weights are identical between them; only seven neutrals, the accent, the grain opacity, and the image filter change.
The dark accent is ember rather than the paper register's cobalt, because cobalt measures 3.87 against the plate ground and fails AA for body links. That change is arithmetic, not taste.
Architecture
- Astro with output: 'static', Vercel adapter, TypeScript strict
- All design tokens are CSS custom properties in one file. A stylelint rule fails the build on any colour literal written anywhere else.
- Content is generated at build time from the Obsidian vault into typed JSON, validated with Zod. The vault stays gitignored; the generated JSON is what a clean clone builds from.
- Images through Cloudinary with AVIF and a WebP fallback, intrinsic dimensions on every image
- Contact form over Gmail SMTP, with a honeypot, a signed timing check, and Cloudflare Turnstile
Previous version
The first portfolio, live from July 2025 to September 2026, was a TRON: ARES-inspired single page: pure black, neon red, glassmorphism, and an interactive 3D Mobius strip built with Three.js and react-three-fiber on Next.js 16. It had a hidden /classified route holding the photo archive.
It was replaced rather than iterated on. The measured reason: the Next App Router ships roughly 99.5 KB of gzipped framework JavaScript on every route before any project code exists, on a site that is text, rules, and photographs.
karthikyammanur.me was registered via Namecheap in August 2025, expired August 2, 2026 when auto-renew was off, and was re-purchased August 20, 2026. It is the portfolio link on the resume, on LinkedIn, and on every application submitted over the past year.
Full stack
Astro · TypeScript · Tailwind CSS 4 · Vanilla TypeScript · Cloudinary · Nodemailer · Cloudflare Turnstile · Playwright · Vitest · Lighthouse CI · Vercel
Results
Lighthouse performance, mobile throttled | 100 across every audited route | |
Lighthouse accessibility | 100, zero suppressed rules | |
Cumulative layout shift | 0.000, including the 184-image archive | |
Client JavaScript, site total | Under 2 KB gzipped | |
Largest Contentful Paint | Approximately 1.06 to 1.13s across routes | |
Static pages generated | ~30 | |
Contact form round trip, deployed | 1.11s |