Personal site and writing of Ömer Çelik — open-source products and essays.
Built with Next.js 16 (App Router, static export), React 19, Tailwind CSS v4, next-intl and MDX.
npm install
cp .env.example .env.local # optional: add a GITHUB_TOKEN
npm run dev # http://localhost:3000
npm run build # static site in ./out
npm run check # lint + typecheck + format check + tests (as CI)
npm run format # apply Prettier- CI —
.github/workflows/ci.yml, on every push tomainand every pull request: lint, typecheck, format check, tests, then the static build. The build fails if any product card couldn't be fetched, and the job checks the key files inout/exist. - Dependabot —
.github/dependabot.yml, weekly. Minor and patch updates arrive grouped (Next.js and its ESLint config together); a major version arrives as its own pull request. GitHub Actions are grouped. - Tests — Vitest,
src/**/*.test.ts: heading ids match rehype-slug, tag slugs, the SEO helpers, and the UI messages (no empty strings or lists). - Tooling — Node 24 (
.nvmrc), Prettier defaults,.editorconfig. Essays undercontent/are left out of formatting on purpose.
- Static export —
output: "export": every page is plain HTML generated at build time. There is no server and no proxy/middleware. - Language — the interface is English, with no locale prefix in URLs. UI
copy lives in
src/i18n/messages/en.json(next-intl, for ICU plurals and rich text). An article can be written in Turkish: itslangfrontmatter sets thelangattribute on its text and the locale of its social card. - Theme — follows the operating system until the visitor picks light or dark with the toggle.
- Design tokens — colours, radius and elevation live in
src/app/theme.css; the type scale (text-meta…text-display) insrc/app/globals.css. Use the scale rather than arbitrary sizes. - Motion — CSS first:
.introfor page-load entrances,.revealfor scroll-linked ones (animation-timeline: view()). Content is visible without JavaScript, andprefers-reduced-motionswitches it all off. - Signature diagram —
src/components/diagram/: an isometric stack of architecture layers in CSS 3D (no WebGL), calm by design — nothing moves on its own. Pointing at a layer opens the stack there (the layers above lift and draw back, the focused one comes forward and lights up, its description shows below); the legend does the same on hover or keyboard focus and pins a layer on click; on touch, tapping a layer pins it. Invisible hit surfaces at the layers' resting positions decide which layer is under the pointer, so the focus doesn't jump while the stack opens. It opens a little as the page scrolls and never tilts, so the layers stay where the pointer expects them. Without JavaScript or with reduced motion it's a still diagram. - SEO —
src/lib/seo.ts: every page gets its title, description, canonical, Open Graph and X card frompageMetadata/articleMetadata(Next merges metadata shallowly, so pages set the whole block). Structured data: Person + WebSite everywhere, ProfilePage on About, BlogPosting + BreadcrumbList on articles. Social cards are real.pngroutes (src/app/og.png,src/app/og/[slug]/card.png) so static hosts serve them as images. Tag pages arenoindex, follow. - Type — Geist for the interface, Newsreader (serif) for essay titles and pull quotes.
- Products — read from the GitHub API at build time; curate the list in
src/config/products.ts. SetGITHUB_TOKENwherever the build runs: unauthenticated requests are limited to 60 an hour per IP, and a failed request drops that card from the build (with a[products]warning in the build log). - Contact — no backend: the form opens the visitor's own mail app with the message filled in, and says so.
Create content/writings/<slug>.mdx:
---
title: "My post"
description: "One-line summary."
date: "2026-09-11"
lang: "en" # en | tr — the language the piece is written in
tags: ["architecture"]
draft: true # optional: visible in `npm run dev` only
series: "Golden paths" # optional, with seriesOrder: 1
---
Your MDX content…What an article can use:
-
Code — fenced blocks are highlighted with Shiki (light + dark). Add a file name and highlighted lines with
```ts title="lib/x.ts" {2-3}, and line numbers withshowLineNumbers. Every block gets a copy button. -
Callouts —
<Callout type="note | tip | warning" title="…">…</Callout>. -
Figures —
<Figure src="/writings/diagram.png" alt="…" caption="…" />, with the image underpublic/writings/. -
Pull quotes —
<PullQuote cite="…">The line you want remembered.</PullQuote>, set large in the serif. -
Layer diagrams — the hero's 3D stack, for your own systems:
<LayerStack title="Where each guarantee lives"> <Layer label="Spec" detail="manifest.yaml" note="What must be true." /> <Layer label="Code" detail="generated" /> </LayerStack>
String props only — the MDX pipeline doesn't evaluate
{…}expressions. -
Featured —
featured: truein the frontmatter leads the home page with that essay. -
GFM — tables, task lists and footnotes (
[^1]). -
Table of contents — appears automatically from three h2/h3 headings up.
-
Comments — Giscus (GitHub Discussions), one thread per post.
| What | Where |
|---|---|
| Name, email, social links, comments | src/config/site.ts |
| Which repos show as products | src/config/products.ts |
| Colours, radius, shadows | src/app/theme.css |
| Type scale, motion, code styling | src/app/globals.css |
| UI copy | src/i18n/messages/en.json |
Same setup as the *.qorpe.com sites: wrangler.jsonc serves ./out as
Worker static assets, with 404.html for unknown paths.
- Connect the repository in Cloudflare Workers Builds. Build command:
npm run build; deploy command:npx wrangler deploy. - Add a
GITHUB_TOKENbuild variable — a token with no permissions is enough to read public repositories. - Attach the
omercelik.devcustom domain to the Worker.
The site ships everything search engines read (sitemap, canonicals, structured data); these steps tell them it exists:
- Google Search Console — add a Domain property for
omercelik.dev, verify with the DNS TXT record (in Cloudflare DNS), then submithttps://omercelik.dev/sitemap.xml. Use URL inspection → Request indexing for the home page and each new essay. - Bing Webmaster Tools — import the site from Search Console (Bing also feeds DuckDuckGo and others).
- Link back to the site from GitHub, LinkedIn and X profiles — the same
URLs listed as
sameAsin the Person structured data. Consistent profiles are what tie searches for the name to this site. - Check a page with Google's Rich Results Test and share one link on LinkedIn/X to confirm the social card.