how i built this
Behind the scenes of inkfangs.com
"I didn't use a template. I didn't hire someone. I built this from scratch because I wanted to prove—to myself and to you—that I can."
— Noufah, explaining why this took longer than expected
The Tech Stack
Frontend Framework
Next.js 16
App Router, Turbopack, Server Components
Why? I wanted to learn the latest Next.js patterns and App Router. Plus, built-in routing and API routes made development faster.
Language
TypeScript
Type-safe development
Why? Catch errors before runtime. Better autocomplete. More confidence when refactoring.
Backend Touchpoints
Turso/libSQL + Resend
Contact persistence, rate limiting, and email notifications
Why? The site only needs a small backend, so I kept it boring: store contact messages, send the important emails, and rate-limit public forms.
Styling
Tailwind CSS
Utility-first CSS
Why? Faster prototyping. Consistent design system. Easy responsive design.
Creative Inspiration: Doves '25 on Blank Canvas
Where the Aesthetic Came From
While most developer portfolios pull inspiration from Dribbble or Awwwards, mine came from music: Hindia's album Doves '25 on Blank Canvas.
The album's visual identity—newspaper-style typography, contemplative minimalism, editorial layouts—resonated with how I wanted to tell my story. Not flashy. Not competing for attention. Just thoughtful, intentional, and real.
What drew me: Indonesian artist bringing authenticity to global aesthetics. Contemplative, not performative. Minimalist but emotionally rich.
The Symbolism: Doves as Freedom
In the album, doves write on a blank canvas—they represent freedom, new beginnings, and creating something from nothing. Birds that fly where they choose, unbound by expectations.
In the tech world, this resonates deeply. We're often caged by:
- • Templates and frameworks that all look the same
- • Comparison culture and imposter syndrome
- • "Right way" to build, "best practices" that stifle creativity
- • Expectations of what a CS student portfolio "should" be
Doves don't ask permission to fly. They just fly.
This portfolio is my blank canvas. My code. My story. My freedom to create something authentically mine, even if it doesn't look like everyone else's.
Visual Translation
- Serif fonts for headlines → Newspaper masthead energy
- Thoughts and builds pages → Editorial design homage
- Muted palette → #F5F5F5 (blank canvas), #000000 (ink), #666666 (newsprint)
- Generous whitespace → Room to breathe and think
Content Philosophy
- Storytelling over specs → Narrative-first approach
- Questions over answers → "Building questions, not just systems"
- Vulnerability as strength → Honest editorial voice
- Indonesian values → Thoughtful, communal, reflective
"Doves writing on a blank canvas—new beginnings, fresh starts, creating something from nothing."
Listen: Doves '25 on Blank Canvas by Hindia →Development Timeline
v1.0 — Initial Setup
Scaffolded the project with Create Next App. Built the landing page, navigation, and the Hindia-inspired editorial aesthetic.
v2.0 — Complete Pages & Community
Added the core public pages: Builds, Thoughts, Worklog, Contact, and Community. Earlier experiments were cut when they stopped serving the current story.
v3.0 — Gallery Exhibition & New Content
Major update to builds page with gallery exhibition layout. Added new project content, screenshots, and refined the editorial design across all pages.
v4.0 — The Revamp (Current)
Organization-based worklog with expandable cards. Contact page rework. Content deduplication across all pages. Turso-backed contact form with Resend notifications. Translated all content to English.
Technical Challenges & Solutions
Challenge #1: Theme Without Flash
Problem: Dark mode looked right after hydration, but the first paint could briefly show the wrong theme.
Solution: Read the saved theme before React hydrates and apply the class immediately:
const themeScript =
"(function(){var t=localStorage.getItem('theme');" +
"if(t==='dark')document.documentElement.classList.add('dark');})();";Lesson learned: Tiny timing details can make a polished interface feel careless if ignored.
Challenge #2: Touch vs Hover Navigation
Problem: Mobile breakpoints alone were not enough. A small touch laptop and a large phone should not be treated the same way.
Solution: Use interaction capability for navigation behavior and `usePathname()` only for route active states.
<nav className="[@media(hover:none)]:hidden">...</nav> <button className="[@media(hover:hover)]:hidden">...</button> const isActive = (href: string) => pathname === href;
Lesson learned: Responsive design is not only screen size. Input method matters.
Challenge #3: Small Backend, Real Boundaries
Problem: The contact form needed to be simple for visitors, but still safe enough for production traffic.
Solution: Use a tiny Next.js API surface with Turso persistence, Resend notifications, honeypot fields, safe JSON parsing, and persistent rate limits.
const body = await readJsonBody(request);
if (!body) {
return NextResponse.json({ error: "Invalid JSON body." }, { status: 400 });
}
await isRateLimited({ request, action: "contact", limit: 3, windowMs });Lesson learned: A small backend still needs clear failure modes and abuse protection.
Tools & Environment
Code Editor
Extensions: ESLint, Prettier, Tailwind IntelliSense
Design
Mostly coded directly in browser
Version Control
GitHub repo with CI checks on push
Deployment
Vercel deployment from the production branch
Package Manager
Lean dependency set: Next, React, Motion, Turso, Resend
AI Assistant
For brainstorming, debugging, learning
By The Numbers
3
API routes
2
Database tables
8+
Public pages
7
Editorial articles
100+
Hours invested
0
Templates used
CI
Lint, test, build
100%
Built by me
What I Learned
- •Next.js App Router is powerful but has a learning curve. Server Components vs Client Components took time to understand.
- •Small backends still deserve care. Validation, rate limits, and clear config errors are not optional just because the site is personal.
- •Accessibility matters from day one. Adding it later is harder than building it in from the start.
- •Design systems save time. Defining colors, typography, and spacing early made development faster.
- •Storytelling > Feature lists. People connect with narrative, not just technical specs.
- •Iteration is essential. Old ideas can be removed when they no longer support the story.
Want to See the Code?
This portfolio is open source. See every line, every component, every decision.
"Building this portfolio taught me more than any tutorial ever could. Every bug was a lesson. Every iteration was growth."
— Noufah, 2026