Site overhaul: static export, design system, 3D stack, SEO, CI - #2
Merged
Conversation
- theme toggle: useSyncExternalStore instead of setState in an effect - typewriter: schedule the phrase advance instead of a synchronous setState - lang switcher: move the cookie write to module scope, use location.assign - not-found: next/link instead of a raw anchor - robots, sitemap, OG images: force-static (+ full locale x slug params) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tools Foundation - output: "export" served as Worker assets (wrangler.jsonc, like the *.qorpe.com sites); proxy removed, root page picks /en or /tr in the browser - UI locales cut to English + Turkish, every URL prefixed; each locale is canonical for itself, articles canonical in the language they're written in - products: owner/repo support (goldpath, specdrift, mediant, mockifyr moved to qorpe), fetched once per process, loud [products] warnings on failure - placeholder bio replaced, example posts marked draft, starter SVGs removed Design - type scale tokens (text-meta ... text-display) replace arbitrary sizes - one Label and one Tag style site-wide; PageHeader for inner pages - CSS-only motion: .intro on load, scroll-driven .reveal; content visible without JS, reduced motion respected - person-first hero with a single, fully server-rendered h1 - mobile header no longer shows Contact twice; skip link; honest mailto form Writing - block vs inline code fixed, copy button, file titles, highlighted lines - heading anchors on hover, TOC from three headings, author card, newer/older navigation, Callout and Figure MDX components, footnotes - JSON-LD (Person/WebSite, BlogPosting), RSS links, Giscus thread per slug Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signature - ArchitectureStack (src/components/diagram): layers of a system stacked in CSS 3D, no WebGL. Tilts with the pointer (TiltScene, eased rAF), opens as the page scrolls (scroll-driven --spread), runs a signal down the layers; idle sway on touch, a still diagram under reduced motion or without JS - labels sit on each layer's front edge so every layer reads, not just the top - hero becomes two columns: the person and headline, then the stack - the same component is available to essays as <LayerStack><Layer/></LayerStack> Reading - article column centred on a three-track grid, TOC in the right margin - TOC follows the reader (active section highlighted) - reading progress hairline, CSS scroll-driven - Newsreader serif for essay titles and pull quotes (<PullQuote>) - `featured: true` leads the home page's writing section with that essay Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ll SEO Stack - one layer in focus at a time: it slides forward and lights up, the layers above fade, and its description shows below; plays top to bottom while in view (paused off-screen and in background tabs) - steerable: mouse height over the stack scrubs layers, tap steps forward, a legend selects a layer (keyboard included); tilt merged into StackScene - hero layers get EN/TR descriptions; article LayerStack uses the notes Standards - faint text raised to WCAG AA (4.57:1 light, 5.45:1 dark) - text-control (14px) for buttons, text-field (16px) for inputs (no iOS zoom) - remaining off-scale sizes and weights removed (404s, logo, portrait) SEO - pageMetadata / articleMetadata: per-page canonical, hreflang, og:url, og:locale, X cards, RSS link; home title "Ömer Çelik — Software Engineer" - social cards as real .png routes (og.png, og/<slug>/card.png) - JSON-LD: Person (+alternateName, knowsAbout), ProfilePage on About, BlogPosting (+image, dateModified, publisher) and BreadcrumbList - robots with max-image-preview, tag pages noindex/follow, theme-color, apple-touch-icon, 160px avatar for small portraits - meta descriptions within ~140 characters - README: SEO setup and search-engine steps after the first deploy Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hovering the lower layers faded everything above to 16% and stacked their connectors into a floating blue line. Now the stack fans open at the focus: layers above lift and draw back, the active layer comes forward and lights up, and the rest only dim their contents — the structure stays visible. A single pulse crosses the opened gap into the new layer. Scrubbing gets hysteresis so resting near a boundary doesn't flicker between two layers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- the active layer now rises above the stack and drifts sideways, so nothing covers it and every line on it reads; the others stay in place, dimmed - connectors removed: the lifted-out card left a blue line pointing upward - the stack is anchored at its bottom layer, so opening it (scroll or focus) only grows upward and keeps a constant gap to the legend - every layer shows the same three key/value lines - a stack that starts off-screen begins playing as soon as it's in view Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Back to the open-the-stack focus (layers above lift and draw back, the focused one comes forward), without what made it messy: - nothing moves on its own: no autoplay, no idle sway, no background pulse; pointing at the stack or the legend opens it, leaving returns it to rest (click/tap pins a layer) - every line on a layer sits along its front edge, the part the open stack reveals, so the focused layer's contents read in full - tilt only while the pointer is over the stack, and a few degrees at most - the caption becomes the resting state of the description line: two lines under the stack instead of three Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Hover mapped the stack's height onto five equal bands, which didn't line up with the layers as drawn in 3D — the focus changed a little above each card. - invisible hit surfaces, one per layer at its resting position, decide which layer is under the pointer; the visible layers stay free to move - hit surfaces in reverse DOM order: Chrome paints the 3D scene by depth but resolves overlapping hit targets by DOM order - no tilt: the scene turning toward the pointer moved each card away from it while you reached for it - the focused layer steps forward 12px (was 18) so it stays off the next layer's hit strip - tapping a layer on touch pins that layer Verified with real pointer moves onto each layer's label: every one selects its own layer, top to bottom and back up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ates Security - Next.js 16.2.10 → 16.3.5 (with eslint-config-next) and `npm audit fix`: npm audit reports 0 vulnerabilities (was 8, one critical — none of them reachable from the static export, but the build machine runs this code) Checks - CI (.github/workflows/ci.yml): lint, typecheck, format check, tests, then the static build — failing if a product card couldn't be fetched — and a check that the key files in out/ exist - Dependabot: weekly; minor/patch grouped (Next.js + its ESLint config together), majors as their own pull requests; GitHub Actions grouped - Vitest: heading ids vs rehype-slug, tag slugs, SEO helpers, EN/TR message parity; scripts typecheck, test, format, format:check, check Tooling - Node 24 (.nvmrc, engines >=22.12, @types/node 24), Prettier defaults applied to the codebase (essays under content/ excluded), .editorconfig - .claude/launch.json committed, local agent settings ignored Cleanup - unused --hairline token and SiteConfig type removed; SEO internals no longer exported - contact form uses a literal mailto: (Next 16.3 lint rule) - [locale] segment: dynamicParams = false, so an unknown first segment is a 404 instead of an error Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Browsers and tools ask for /favicon.ico whatever the page declares; with only icon.svg the request fell through to the [locale] route (a 404, and an error in the dev server log). Two RGBA PNG sizes (32, 48) in an ICO — Next's image pipeline rejects RGB-only PNGs inside an icon. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Drop the /en and /tr routes: pages live at the root, no locale prefix, no language switcher, no hreflang. next-intl stays for UI copy with a fixed English locale, so every page is still statically rendered. - Articles keep their own language: lang attribute on title, description and body; og:locale from the article (tr_TR for Turkish pieces). - lib/seo: absoluteUrl replaces localeUrl; sitemap and feed list single URLs. - Theme defaults to the operating system; the toggle still overrides it. - 404 gets its own title and a consistent noindex. - Tests, CI output checks and README updated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The site overhaul: a static export ready for Cloudflare Workers Builds, a new design system with the signature 3D architecture stack, full SEO, and a maintainable repo.
What changes
output: "export"): plain HTML in./out, served bywrangler.jsoncas Worker static assets with404.html. Product cards are fetched from the GitHub API at build time (GITHUB_TOKEN).langattribute,og:locale).<LayerStack>), with accurate hover/tap/keyboard focus..pngsocial cards, JSON-LD (Person, WebSite, ProfilePage, BlogPosting, BreadcrumbList), sitemap, robots, noindex tag pages and 404..nvmrc,.editorconfig, README.Verified locally
npm run check: lint, typecheck, format and 17 tests pass.next build: 23 static routes, all 5 product cards, key files present inout/.After merging
GITHUB_TOKENbuild variable (see README).🤖 Generated with Claude Code