A portfolio that reads as one continuous document and deploys as static files.
Live site Β· Flutter Web first-frame issue Β· Engine patch
| You edit | Visitors get | CI proves |
|---|---|---|
| Identity, biography, work, evidence, links, and metadata in one validated JSON document. | One accessible, responsive page with real deep links and reduced-motion support. | The Wasm/JavaScript bundle, hosting headers, clean-template reset, and browser behaviour pass executable checks. |
Prerequisites: Git, Node.js 24.18.0, and Flutter 3.44.6. The exact framework
and engine revisions live in tool/toolchain.json and are verified before a
release build.
The checked-in .nvmrc gives Node version managers the same local version;
npm run verify:toolchain reports any mismatch. Hosted builds use the pinned
Flutter revision in tool/hosted_build.sh.
Choose Use this template above and create your own repository. Do not fork the demo for a personal site: GitHub forks retain the parent history, whereas a repository created from a template starts with one unrelated commit. Forks remain the right path for contributing changes back here. Clone your new repository, then run:
git clone https://github.com/your-name/your-portfolio.git
cd your-portfolio
npm ci
npm run setup:browsers
flutter pub get
npm run portfolio:init
npm run build:release
node tool/serve_web.mjsOpen http://127.0.0.1:4173. The preview serves the exact release directory
with the security headers and SPA fallback used in production. Press Ctrl+C
to stop it. During normal UI work, flutter run -d chrome remains the faster
hot-reload loop.
GitHub's template-repository guide explains the history and contribution differences.
The initializer is a real reset, not a search-and-replace checklist. It asks for
your public identity, role, contact, canonical domain, headline, and focus;
removes the original ownerβs optional work, experience, and contribution data;
deletes the demo work artifacts, source captures, and inherited build/web
release; then regenerates the social card, source manifest, README record, SEO,
JSON-LD, web manifest,
sitemap, robots file, analytics include, and server policy. The reset is
transactional: a failed renderer or synchronization step restores every touched
file instead of leaving a half-customized repository. When the Git remote is on
GitHub, the live CI badge is rewritten to that repository as part of the reset.
you answer once
β
βΌ
assets/content/portfolio.json
ββββΊ Flutter document
ββββΊ search + social metadata
ββββΊ README engineering record
ββββΊ manifest + sitemap + robots
ββββΊ hosting policy + social card
Customization guide Β· Content contract Β· Deployment guide
The app is static and has no backend lock-in.
| Host | Shortest path |
|---|---|
| GitHub Pages | push main; the included workflow builds with the repository-aware base path |
| Firebase Hosting | npm run deploy -- firebase --project <id> |
| Netlify | connect the repo or run npm run deploy -- netlify |
| Cloudflare Pages | npm run deploy -- cloudflare --project <name> |
| Vercel | import the repo or run npm run deploy -- vercel |
| Docker / VPS | npm run deploy -- docker --image portfolio:latest |
Firebase, Netlify, Cloudflare Pages, Vercel, and Nginx preserve SPA navigation,
the generated content-security policy, and the cross-origin isolation headers
used by threaded SkWasm. GitHub Pages cannot set custom response headers, so its
build uses the compatible single-threaded/JavaScript fallback.
Custom domains require no Dart change: edit site.url, run
npm run sync:content, then point DNS at the host.
The deployment guide separates Git-connected builds from direct uploads and gives custom-domain changes an explicit cutover order. In particular, Cloudflare Pages treats Git integration and Direct Upload as different project types; choose the one you intend to keep before the first deployment.
Provider-by-provider instructions
Most portfolio templates start with cards and later bolt on data. This one starts with a document contract:
- Optional chapters genuinely disappear; there are no empty demo sections.
- Work artifacts must be local, accessible, dimensioned, and backed by linked evidence.
- Professional claims remain outside presentation code and interface-copy catalogs.
- The same measured position controls active navigation, browser history, scene interpolation, and every progress indicator.
- Reduced motion preserves the entire information architecture instead of serving a diminished page.
- Decorative painting listens to frame-adjacent state without rebuilding the semantic document or idling at 60 frames per second.
- The critical HTML shell gives slow Wasm visits meaningful content before Flutter reaches the compositor.
assets/content/portfolio.json is parsed into immutable final classes before
runApp. The parser rejects schema drift, duplicate IDs, invalid URLs, reused
artifacts, incomplete featured cases, malformed colors, and identity/metadata
disagreement.
{
"schema_version": 8,
"site": { "url": "https://example.com" },
"profile": { "name": "Your Name", "role": "Software Engineer" },
"experience": [],
"contributions": [],
"systems": []
}That excerpt is incomplete by design; use the initializer to produce a valid clean document, then follow the field guide to add traceable experience and selected work.
assets/i18n/<locale>.json owns interface chrome only. Complete translated
professional copy belongs in assets/content/locales/<locale>.json, whose
structure must match the canonical document before that locale can be enabled.
This all-or-nothing contract rejects missing fields instead of silently mixing
languages. The initializer removes the demo professional overlays and starts a
new portfolio with English only, so a clone never advertises untranslated
claims.
Accessibility is content, too: display_name.accessible carries the spoken
name, project alt text describes the visible evidence, captions provide
context, and links name their destination. The field guide shows each of these
records; keyboard, reduced-motion, RTL, and responsive behaviour are covered by
the browser suite.
assets/content/portfolio.json
β strict parse before runApp
βΌ
PortfolioDocument ββββββββββββββββ
β
assets/presentation/narrative.json
β chapter order + motif β
βΌ β
NarrativeDocument β
β β
assets/i18n/{locale}.json β
β ordered async loading β
βΌ βΌ
LanguageCubit semantic sections
β β²
assets/content/locales/ β complete professional overlay
{locale}.json ββββββββββββββββββ
β measured bounds
βΌ
AppScrollController ββββββΊ NarrativePosition
β β
β browser history ββββββΊ SceneDirector
β + visible progress β β
βΌ βΌ βΌ
chapter navigation chapter anchors ambient painter
β
βΌ
NarrativeAnchorPath
The document and render loop use different update paths. Content loads once. Frame-frequency scene and pointer changes stay in synchronous listenables. Responsive reflow preserves chapter-relative focus instead of a stale pixel offset, and one early popstate bridge keeps browser history from being consumed twice by Flutter navigation.
The release does not treat Flutterβs first-frame signal as proof that pixels have reached the browser compositor. Its generated HTML shell remains aligned for two browser frames, then leaves exactly once. Ordered User Timing marks separate entrypoint transfer, engine initialization, Flutter rendering, compositor-safe reveal, and shell removal.
# Serve build/web, then sample cold starts and a real full-document scroll.
npm run measure:runtime
# Enforce the checked-in median budget.
npm run verify:runtimeSoftware-rendered headless sessions still record every metric; only explicitly hardware-bound thresholds are reported rather than misjudged against SwiftShader readback latency.
For a portfolio owner, the release gate is one command:
npm run build:releaseIt validates content, derived public files, the Dart source graph, host policy, the dual runtime, and the final bundle. Repository maintainers run the broader suite below before changing the template itself:
npm run verify:toolchain
npm run portfolio:validate
npm run test:template
npm run test:clone
npm run test:release-security
npm run verify:content
npm run verify:hosting
npm run verify:community
npm run verify:source
npm run audit:sources
npm run audit:history
npm run typecheck
dart format --output=none --set-exit-if-changed lib test tool
flutter analyze --fatal-infos
flutter test
npm run build:release
npm run test:visual
npm test
npm run verify:runtimeThe gates cover:
- canonical-content drift and source status;
- every reachable Dart source file;
- immutable renderer assets and same-origin fallbacks;
- Wasm/JavaScript size budgets and Nginx packaging;
- keyboard semantics, reduced motion, locale switching, RTL, deep links, and back/forward history;
- desktop, tablet, and mobile visual baselines;
- cold-start, layout-shift, long-task, and scroll-frame budgets;
- a generated clean portfolio with no inherited identity.
| Change this | To change that |
|---|---|
assets/content/portfolio.json |
professional content and public metadata |
assets/work/ |
real project evidence and compact crops |
assets/i18n/ |
interface translations |
assets/presentation/narrative.json |
chapter motifs and scene order |
lib/app/domain/ |
strict content and narrative contracts |
lib/app/controllers/ |
measured scroll, history, scene, and reflow state |
lib/app/widgets/narrative_stage.dart |
the continuous document trace |
tool/ |
initialization, synchronization, release, and verification |
tests/e2e/ |
browser and visual regression contracts |
The live demo uses the same template with a real professional record. This block is regenerated from the canonical content document; it is evidence for the demo, not starter data inherited by npm run portfolio:init.
Open the current record
Yusuf Δ°hsan GΓΆrgel β Software Engineer. Iβm a software engineer working across Flutter, Dart, Go, and production infrastructure.
Since 2021, I have built and maintained software for mobile devices, tablets, desktop operating systems, and the web. My work includes ERP and point-of-sale products, logistics workflows, digital publishing, backend services, and the release systems around them.
Source status: 2026.07.30.1, verified 2026-07-30 against GitHub, LinkedIn, FugaSoft, Dorse, and Medium.
| Project | Change | Merged | Evidence |
|---|---|---|---|
| Flutter | Return null from RenderProxyBoxMixin.computeDryBaseline when the child has no baseline | 2026-07-30 | Pull request |
| Flutter Packages | Ignore unrecognized SVG font-weight values | 2026-07-28 | Pull request |
| simdjson | Treat 20-digit positive overflows as big integers | 2026-07-28 | Pull request |
| Dart Native Assets | Validate dynamic library architecture from file headers | 2026-07-27 | Pull request |
| Dart MCP | Add the caching hints from the 2026-07-28 protocol revision | 2026-07-27 | Pull request |
| gRPC-Go | Restore the plan9 build by splitting errno matching out | 2026-07-23 | Pull request |
| Retrofit for Dart | Keep the request future in the stream pipeline for ResponseType.stream | 2026-07-21 | Pull request |
| Retrofit for Dart | Guard PartMap helper variables for nullable file parts | 2026-07-21 | Pull request |
| Retrofit for Dart | Make contentType null-safe in newRequestOptions | 2026-07-21 | Pull request |
| Dart MCP | Operate on decoded messages instead of JSON strings | 2026-07-20 | Pull request |
| Shelf | Join multiple Cookie header values with the RFC-correct separator | 2026-07-20 | Pull request |
| Bun | Surface lost SQL migration finalizer errors | 2026-07-17 | Pull request |
| Dart MCP | Add request-scoped message dispatch for MCP servers | 2026-07-17 | Pull request |
| Dart MCP | Separate server feature registration from legacy initialization | 2026-07-15 | Pull request |
| FlutterFire | Make Firebase core loading deterministic on WebKit | 2026-07-15 | Pull request |
| Flutter Form Builder | Reset unknown dropdown initial values on first build | 2026-07-14 | Pull request |
| Drift | Treat SQLite TRUE and 1 defaults as the same schema | 2026-07-14 | Pull request |
| Go Fiber Recipes | Add a Fiber and Asynq background-jobs recipe | 2026-07-12 | Pull request |
| Project | Responsibility | Evidence |
|---|---|---|
| FugaSoft | I work primarily on the Flutter clients and the production concerns around offline data, native integrations, synchronisation, performance, release, and long-term maintenance. | Project |
| Dorse | I developed the Flutter application and React web surface across live vehicle state, maps, REST and WebSocket flows, deployment, and QA coordination. | Project |
| AydΔ±nlΔ±k E-Gazete | At Promob TR, I worked on the Flutter client, API-backed issue flow, localisation, and mobile releases. | Project |
| Bilim ve Γtopya | At Promob TR, I worked on Flutter features, API integration, localisation, and release support. | Project |
| Queue Inspector MCP | I designed the command surface, typed validation, queue adapters, and conservative state-changing operations. | Project |
| Multi-tenant Gateway | I designed and implemented the reference from transport and tenant resolution through policy, persistence, and test coverage. | Project |
| Redis Task Queue | I designed the public API, queue semantics, failure handling, and runnable examples. | Project |
| Constellation Particles | I implemented the painter, pointer interaction, spatial partitioning, and package examples without runtime dependencies. | Project |
| Flutter Web Portfolio | I built and run the Flutter Web site, its external content pipeline, accessibility layer, browser regression suite, and production release. | Project |
- Flutter: Wait for web rendering before the first-frame event β Wait for outstanding scene renders and the next browser frame before dispatching the event; the reviewer has since asked for a lighter completer-based shape.
- MCP Kotlin SDK: Add SEP-2575 request metadata and discovery types β Add typed experimental metadata accessors, server discovery types, polymorphic codecs, and malformed-input coverage.
Focused fixes, measured performance improvements, accessibility work, new host adapters, and locale corrections are welcome. Start with CONTRIBUTING.md; the pull-request template names the same checks CI enforces.
MIT. Keep the license, replace the content, and make the document yours.