Skip to main content
Site Build Details

Colophon

How was this site built? An honest, thorough breakdown of the technology, the decisions behind it, the infrastructure, and the daily measurement series.

Technology Stack

Framework

  • Next.js16.3.6App Router, standalone SSR, Turbopack
  • React19.2.3Server Components + use() hook
  • TypeScript5.9.3Strict mode

Styling

  • Tailwind CSS4.1.18@theme + @custom-variant dark, OKLCH
  • CSS VariablesDesign tokens, runtime theme switch
  • Apple HIG colorsCanonical iOS Blue (#007AFF / #0A84FF dark)

Animation

  • Motion12.38.0LazyMotion + domAnimation strict
  • Lenis Smooth ScrollGPU-accelerated scroll
  • GPU-only propertiestransform + opacity, layout reflow yok

State

  • Zustand5.0.11Hafif client state
  • TanStack Query5.100.10Server state + cache
  • nuqs2.8.8Type-safe URL search params

Data

  • Prisma6.19.2Type-safe ORM
  • PostgreSQL16Engagement + analytics
  • ResendNewsletter delivery

Quality

  • Vitest4.1.11Unit + integration
  • Playwright + axe-core1.58.1WCAG 2.2 AA E2E
  • Lighthouse CIPerf / a11y / SEO budget checks

Deploy

  • Hetzner VPSStandalone Node SSR
  • CloudflareCDN + Brotli + 0-RTT + HTTP/3
  • Let's EncryptTLS 1.3 + HSTS preload

Observability

  • PostgreSQL analyticsFirst-party, consent only, on our own server
  • SentryError tracking, EU region
  • Real User MonitoringCWV via web-vitals lib

Typography

DisplaySF Pro Display100-900 variableApple system font, opsz axis
BodySF Pro Text100-900 variableSame SF Pro, small opsz
MonoSF Mono400 / 500 / 700Code blocks, version numbers

Design Principles

Performance First

Standalone SSR, ISR, dynamic import, GPU-only animation

Purposeful Motion

Every animation guides the user; none is decorative

Accessible by Default

WCAG 2.2 AA, prefers-reduced-motion, 44×44px targets

Native Dark Mode

Synced with the system setting, custom-variant Tailwind v4

Infrastructure

Origin
Hetzner VPS (EU)
Runtime
Node v22.23.3
Process
systemd (portfolio-ssr.service, User=portfolio)
CDN
Cloudflare
Edge cache
s-maxage=60
TLS
1.3 + HSTS preload
DB
PostgreSQL 16

Chose / Rejected

Six decisions that are live on this server right now, each with the option it replaced, the reason it was needed here, what it costs, and the file and line where you can check it. They are all good tools — these cards are about fit, not ranking.

  • ChoseMotion v12

    RejectedGSAP

    Every page is a React tree, and animation state had to live in the same place as component lifecycle. LazyMotion + domAnimation keeps the feature bundle out of the first load, and heavier features such as drag stay scoped to the one component that needs them.

    CostFor timeline-driven, frame-exact choreography GSAP remains stronger; if such a scene is ever needed this decision gets reopened.

    Evidencepackage.json:64

  • ChoseSF Pro (self-host, woff2)

    RejectedInter (Google Fonts)

    The whole site is about the Apple ecosystem, so matching the reader's own system typeface is part of the content. The files are served from our own origin through next/font/local, which removes the third-party font request and its connection cost.

    CostApple's Personal Use licence: the same choice would not be available in a commercial product, and two variable files (including latin-ext) have to be carried.

    Evidencesrc/app/(main)/layout.tsx:34

  • ChosePrisma client → src/generated/prisma

    RejectedThe default node_modules/@prisma/client path

    The standalone SSR output only copies what it can trace. With the client inside the repo under src/, next build treats it as an ordinary module and the whole class of "missing from the .next/standalone copy" failures disappears.

    Costprisma generate becomes mandatory on every schema change and the generated directory sits in the repo tree — which is why it is the first step of the prebuild chain.

    Evidenceprisma/schema.prisma:7

  • Chosesystemd, User=portfolio (non-root)

    Rejectedpm2 running as root

    The SSR process was taken off root on 14 Jul 2026. The unit ships NoNewPrivileges, ProtectSystem, ProtectHome and PrivateTmp; ProtectHome alone means the app can no longer read the deployment credentials under /root. The line above does not hard-code any of this — it asks the unit on every build.

    CostThe build still runs as root, so .next ownership has to be handed back after every build; ProtectSystem=strict and MemoryDenyWriteExecute are deliberately left off because of V8's JIT.

    Evidencescripts/generate-colophon-stack.ts:63

  • ChoseTailwind v4 @theme inline bridge

    RejectedUsing shadcn names as they are

    The project's colour vocabulary is its own semantic tokens (--color-bg-primary, --color-text-primary). shadcn's 19 names — bg-muted, text-foreground and the rest — are bound to those through @theme inline; inline is required because the theme switches at runtime via the .dark / .admin-v2 classes.

    CostUsing a new shadcn name means adding a bridge line and checking its light/dark contrast before it can be used.

    Evidencesrc/app/globals.css:42

  • ChoseContent in PostgreSQL (Prisma)

    RejectedTypeScript constant arrays

    While content lived in TS files every correction required a build, and what the admin panel saw could drift from what the page printed. The database is now the single source; the prebuild step turns those records into static JSON so the page never queries at request time.

    CostA content change only reaches production through a build, and the backup burden moved from the repository to the database.

    Evidencescripts/generate-content-json.ts:200

Measurement Series

A cron on this server has been storing a Lighthouse run per page type every night and a PageSpeed Insights mobile run every morning. These are those files, unedited: the highs and the bad days both.

Home

/

Performance9153–95
Accessibility100100–100
Best Practices100100–100
SEO100100–100
View as a table
CategoryLastMinMaxRuns
Performance91539588
Accessibility10010010088
Best Practices10010010087
SEO10010010088

Blog post

/blog/swift-6-neler-yeni/

Performance9680–98
Accessibility100100–100
Best Practices10096–100
SEO100100–100
View as a table
CategoryLastMinMaxRuns
Performance96809886
Accessibility10010010088
Best Practices1009610087
SEO10010010088

Comparison

/comparisons/swiftui-vs-uikit/

Performance9894–99
Accessibility10097–100
Best Practices10096–100
SEO100100–100
View as a table
CategoryLastMinMaxRuns
Performance98949988
Accessibility1009710088
Best Practices1009610088
SEO10010010088

Project

/projects/part-to-time/

Performance9859–99
Accessibility10096–100
Best Practices100100–100
SEO100100–100
View as a table
CategoryLastMinMaxRuns
Performance98599987
Accessibility1009610087
Best Practices10010010087
SEO10010010087

Lighthouse CI · 2026-08-04 → 2026-11-01 · 90 daily files

PageSpeed Insights — 28-day p75 (mobile)

PSI mobile lab, 4x CPU throttled emulation — not field Core Web Vitals.

PageSpeed Insights 75th percentile by URL
URLScoreLCPTBTCLSn
/903.30 s224 ms0.00128
/blog/apple-foundation-models-framework-on-device-llm/874.35 s170 ms0.00128
/comparisons/swiftui-vs-uikit/884.05 s219 ms0.00128
/quiz/962.85 s112 ms0.07028

2026-10-05 → 2026-11-01 · 28 daily files

License & Credits

  • •Design and content © 2026 Muhittin Çamdalı
  • •Icons: Lucide (ISC) + SF Symbols (Apple sistem)
  • •Fonts: SF Pro + SF Mono (Apple Personal Use License, self-hosted woff2)
  • •For the detailed credits and reference list, see /credits/ page

Infrastructure consulting

Let me set this stack up for your team

Everything on this page — the standalone SSR build, the non-root systemd unit, the token-based theme bridge, the nightly measurement archive — is a setup I install and hand over, documented, on your own servers. If your team is somewhere in the middle of that road, tell me where it is stuck and I will tell you what I would do.

This site is crafted with love and lots of .