yudinugraha.com
A personal website built as both a portfolio and a long-form engineering blog — designed to be fast, content-first, and easy to maintain over years.
Overview
Most portfolio sites are either over-engineered or too shallow. This one is built to do one thing well: present ideas clearly.
The site is structured around four content types: blog posts (long-form engineering writing), notebooks (short technical notes), projects (portfolio pieces), and ventures (business and product work). All content is written in Markdown and MDX — no CMS, no database, just files.
A local-only works section serves as a private workspace for drafts, implementation plans, and work-in-progress writing. It is fully excluded from production builds and never committed to git.
Features
_id suffix convention)rehype-highlightDockerfile and Dockerfile.dev for production and hot-reload development containersTech Stack
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| Language | TypeScript |
| Styling | Tailwind CSS |
| Content | Markdown + MDX (next-mdx-remote, gray-matter) |
| Auth | iron-session + bcryptjs |
| Deployment | Vercel |
| Containerization | Docker + Docker Compose |
Architecture
Content is managed entirely through the filesystem. Each content type lives in its own directory under content/, with frontmatter parsed at build time using gray-matter. MDX files are rendered server-side via next-mdx-remote.
Internationalization is handled by a [lang] segment in the App Router, making English and Indonesian routes first-class citizens without a third-party i18n library.
The works section uses middleware and build-time environment checks to gate the route — it returns 404 in production regardless of whether the content exists on disk.
Challenges
Private draft workspace. The works section needed to be fully invisible in production — not just hidden from navigation, but unreachable by URL and excluded from the build. This required a combination of .gitignore, middleware route protection, and a build-time environment flag.
Multilingual content at scale. Maintaining EN and ID versions of each post without a CMS requires discipline. The convention of co-locating post.md and post_id.md keeps related files together and makes it obvious when a translation is missing.
Long-term maintainability. A personal site needs to be easy to pick up after months away. The decision to keep everything in plain Markdown files — no database, no CMS login, no third-party content lock-in — was deliberate. The content outlives any framework choice.
Design
| Token | Value |
|---|---|
| Primary color | #20215E (Deep Navy Blue) |
| Secondary color | #4CA7D9 (Sky Blue) |
| Font strategy | System font stack for fast rendering |
| Layout | Minimal, content-width constrained, high contrast |