Personal portfolio and developer blog for Yasin Ateş. TanStack Start (SSR) host shell with component-level micro frontend architecture using tuvix.js — every UI component is its own independent micro-app, potentially using a different framework (React, Svelte, Vue).
| Area | Value |
|---|---|
| Primary language | TypeScript (strict mode) |
| Frontend framework | React 19 + TanStack Router + TanStack Query |
| Build tool | Vinxi (Vite-based) |
| SSR | TanStack Start (full SSR — SEO critical) |
| Styling | CSS Modules (.module.scss) — Tailwind removal in progress |
| Test framework | Vitest |
| Module system | ESM only |
| Deployment | Netlify |
| Micro frontend | tuvix.js — component-level, multi-framework |
| CI performance | lighthouse-guard GitHub Action |
Every UI section is its own micro-app. Routes are thin shells. Components live inside
src/micro-apps/{name}/ with their own hooks, utils, and sub-components.
Route (thin shell)
└─ TuvixApp (SSR bridge)
└─ micro-apps/{name}/App.tsx ← main component
└─ components/ ← sub-components, each with .module.scss
src/
├── routes/ # TanStack Router — thin shells ONLY
│ ├── __root.tsx # Root layout — mounts header + footer micro-apps
│ ├── index.tsx # Home — mounts 5 micro-app containers
│ ├── hakkimda.tsx
│ ├── projeler.tsx
│ ├── github.tsx
│ ├── youtube.tsx
│ ├── blog.tsx
│ ├── $postId.tsx
│ └── iletisim.tsx
│
├── micro-apps/
│ ├── _shared/
│ │ ├── queryClient.ts # Singleton TanStack QueryClient
│ │ └── eventBus.ts # Typed cross-app event bus (NEW)
│ │
│ ├── header/ [React] # TanStack Router integration
│ ├── footer/ [Svelte] # Static — Svelte showcase
│ │
│ ├── hero/ [React] # Home: animated hero section
│ ├── about-preview/ [React] # Home: short bio + tech stack preview
│ ├── projects-preview/[React] # Home: featured GitHub/YouTube/other
│ ├── blogs-preview/ [Svelte] # Home: recent blog posts — Svelte showcase
│ ├── contact-preview/ [Vue] # Home: CTA card — Vue showcase
│ │
│ ├── about/ [React] # /hakkimda full page
│ │ └── components/
│ │ ├── ProfileCard.tsx + ProfileCard.module.scss
│ │ ├── TechGrid.tsx + TechGrid.module.scss
│ │ └── SocialLinks.tsx+ SocialLinks.module.scss
│ │
│ ├── github/ [React]
│ │ └── components/
│ │ └── GithubCard.tsx + GithubCard.module.scss
│ │
│ ├── youtube/ [React]
│ │ └── components/
│ │ └── YoutubeCard.tsx + YoutubeCard.module.scss
│ │
│ ├── projects/ [React]
│ │ └── components/
│ │ ├── GithubProjectCard.tsx + GithubProjectCard.module.scss
│ │ ├── YoutubeVideoCard.tsx + YoutubeVideoCard.module.scss
│ │ └── OtherProjectCard.tsx + OtherProjectCard.module.scss
│ │
│ ├── blog-list/ [React]
│ │ └── components/
│ │ ├── BlogCard.tsx + BlogCard.module.scss
│ │ └── TabFilter.tsx + TabFilter.module.scss
│ │
│ ├── post-detail/ [React]
│ │ └── components/
│ │ ├── PostContent.tsx + PostContent.module.scss
│ │ └── RelatedPosts.tsx + RelatedPosts.module.scss
│ │
│ └── contact/ [Vue] # /iletisim full page
│ └── components/
│ ├── ContactInfo.vue + ContactInfo.module.scss
│ └── SocialLinks.vue + SocialLinks.module.scss
│
├── hooks/ # TanStack Query hooks (shared, server-agnostic)
├── utils/ # Pure utility functions
├── constants/ # Static data
├── styles/ # Global CSS (app.css — Tailwind removal WIP)
├── client.tsx # Client entry — hydrate + orchestrator init
├── ssr.tsx # SSR entry
└── router.tsx # Router config
// routes/iletisim.tsx — thin shell (ONLY this, nothing else)
import { createFileRoute } from '@tanstack/react-router'
import { TuvixApp } from '@tuvix.js/react'
import { ContactApp } from '~/micro-apps/contact/App'
export const Route = createFileRoute('/iletisim')({
head: () => ({ meta: seo({ title: '...', description: '...' }) }),
component: () => <TuvixApp name="contact-app" App={ContactApp} />,
})// micro-apps/_shared/eventBus.ts
type EventMap = {
'hero:cta-clicked': { target: 'projects' | 'contact' }
'blog:post-selected': { postId: string; title: string }
'blog:filter-changed': { tab: 'all' | 'medium' | 'devto' }
'nav:route-changed': { pathname: string }
}| Micro-app | Framework | Reason |
|---|---|---|
header |
React | TanStack Router hooks required |
footer |
Svelte | Static — ideal Svelte demo |
hero |
React | Complex CSS animations |
about-preview |
React | Tech grid with inline SVGs |
projects-preview |
React | TanStack Query (GitHub + YouTube) |
blogs-preview |
Svelte | Native fetch — Svelte showcase |
contact-preview |
Vue | Simple CTA — Vue showcase |
about |
React | Full tech stack, social links |
github |
React | TanStack Query |
youtube |
React | TanStack Query |
projects |
React | 3 data sources |
blog-list |
React | Tab state + 2 APIs |
post-detail |
React | SyntaxHighlighter |
contact |
Vue | Form-like — Vue showcase |
@tuvix.js/react — React adapter (SSR-ready)
@tuvix.js/svelte — Svelte adapter (to be verified/built)
@tuvix.js/vue — Vue adapter (to be verified/built)
@tuvix.js/core — Orchestrator
@tuvix.js/event-bus — Cross-app typed events
| Gap | Status |
|---|---|
@tuvix.js/svelte SSR adapter |
Unknown — verify |
@tuvix.js/vue SSR adapter |
Unknown — verify |
| Built-in event bus API | Exists (@tuvix.js/event-bus) — test |
| Non-React SSR hydration | Unknown — test |
# Development
npm run dev # Start dev server (Vinxi)
# Build & Deploy
npm run build # Production build
npm start # Start production server
# Type checking
npx tsc --noEmit # TypeScript type check
# Tests
npm test # Run all tests (Vitest)
npm run test:watch # Watch mode
npm run test:coverage # Coverage reportstrict: truein tsconfig — no exceptionsanytype is FORBIDDEN — useunknownwithinstanceofguards- Catch clauses: always
catch (e: unknown), nevercatch (e: any)
// ✅ Correct
try {
const data = await fetchData()
} catch (e: unknown) {
if (e instanceof Error) console.error(e.message)
}
// ❌ Wrong
catch (e: any) { console.error(e.message) }- Do NOT add new Tailwind classes — removal in progress
- All components use CSS Modules (
.module.scssco-located) - No inline styles with magic numbers — use CSS custom properties
- SCSS files live inside the micro-app directory, not in
src/routes/
- Functional components only — no class components except ErrorBoundary
- Data fetching: TanStack Query hooks in
src/hooks/only - No direct API calls in components
- Route files:
createFileRoute()+TuvixApponly — no component logic
- Every route must have
head()returning meta tags viaseo()util - No browser-only APIs at top level (
window,document,localStorage) - Guard:
if (typeof window !== 'undefined') { ... } - Canonical URLs must be preserved on all routes
Non-React frameworks (Svelte, Vue, Angular) cannot render during SSR unless explicitly pre-rendered. The correct pattern:
// Route loader: pre-render on server
loader: async () => ({
ssrHtml: await renderSvelteToString(FooterSvelte), // Svelte
// OR: await renderVueToString(ContactVue), // Vue
// OR: await renderAngularToString(Component, {...}) // Angular
})
// Bridge component: static import, accepts ssrHtml prop
import FrameworkComponent from './Component.svelte' // static import
export default function Bridge({ ssrHtml = '' }) {
const ref = useRef(null)
useEffect(() => {
// mount with hydrate:true so framework reuses SSR HTML
new FrameworkComponent({ target: ref.current, hydrate: true })
}, [])
return <div ref={ref} dangerouslySetInnerHTML={{ __html: ssrHtml }} suppressHydrationWarning />
}Server utilities (import from sub-path, not main):
- Svelte:
renderSvelteToStringfrom@tuvix.js/svelte/server - Vue:
renderVueToStringfrom@tuvix.js/vue/server - Angular:
renderAngularToStringfrom@tuvix.js/angular/server
Build notes:
@tuvix.js/svelte/serverusesnew Function('m','return import(m)')to loadsvelte/serverat runtime — prevents Vite/Rollup CJS static scanner from failing whensvelte/server(Svelte 5 API) is absent. Svelte 4 falls back toApp.render().- Svelte components must be compiled with
hydratable: true(set inapp.config.tssvelte({ compilerOptions: { hydratable: true } })) forhydrate: trueto work at runtime. @angular/animationsmust be installed as a peer dep for Angular builds
- Apps communicate ONLY via
@tuvix.js/event-busor_shared/eventBus.ts— no cross-app imports - Each micro-app handles its own data fetching via hooks
- Each component in
components/has its own.module.scss - No
<QueryClientProvider>inside micro-apps — TanStack Router provides it at root
- SSR must keep working — SEO-critical; breaking SSR is a critical failure
- No
anytypes — TypeScript strict mode is non-negotiable - No new Tailwind — migrating away from it
- Visual parity — do not change appearance without explicit instruction
- No
console.login production code — remove before committing - Lighthouse scores — must not degrade
- No cross-micro-app imports — only event bus communication
- Route files are thin shells — no component logic in routes
- No dynamic
import()in application code — use static imports at file top. Dynamic imports insideuseEffector component bodies break SSR (render empty HTML server-side). Non-React framework components (Svelte, Vue, Angular) must be SSR-rendered via route loaders + tuvix.js server utilities, not deferred with lazy imports.
Framework: Vitest
Test location: src/__tests__/
Coverage target: ≥ 70% on src/hooks/ and src/utils/
Priority test targets:
src/utils/seo.ts— pure functionsrc/utils/slugify.ts— pure function with Turkish characterssrc/hooks/use*.ts— query hooks with mocked axiossrc/micro-apps/_shared/eventBus.ts— typed bus logic
npm install --save-dev vitest @vitest/coverage-v8 @testing-library/react @testing-library/jest-dom- Brainstorm —
/superpowers:brainstormingbefore any non-trivial feature - Enhance —
/prompt-enhancer→ generates BRAID graph for complex tasks - Solve —
/braid-solverexecutes the BRAID graph deterministically - Plan —
/superpowers:writing-plans→ structured implementation plan - TDD —
/superpowers:test-driven-development→ write failing test first - Implement — follow
.claude/references/coding-standards.md - Review —
/wtf-code-reviewerafter every implementation (mandatory) - Verify —
/superpowers:verification-before-completion - Commit — descriptive commit message
- Delta review —
/code-review-graph:review-deltaafter every commit
/microfrontend-migratoragent for component extraction/prompt-enhancer→/braid-solverfor complex migrations- Always verify SSR after migration
- Test event bus communication between apps
/superpowers:dispatching-parallel-agents— spawn independent subagents- Each agent works on one micro-app in isolation
- Merge when all pass
/wtf-code-reviewer
| Situation | Invoke |
|---|---|
| Before any non-trivial feature | /superpowers:brainstorming |
| Complex multi-step tasks | /prompt-enhancer → /braid-solver |
| Planning implementation | /superpowers:writing-plans |
| Starting implementation | /superpowers:test-driven-development |
| Debugging a failure | /superpowers:systematic-debugging |
| Parallel independent tasks | /superpowers:dispatching-parallel-agents |
| Before claiming done | /superpowers:verification-before-completion |
| After any implementation | /wtf-code-reviewer (mandatory) |
| After each commit | /code-review-graph:review-delta |
| Before merge | /code-review-graph:review-pr |
| Migrating component to micro app | /microfrontend-migrator |
| TypeScript review | /everything-claude-code:python-review (adapt) |
| Security-sensitive code | /everything-claude-code:security-review |
| Looking up library docs | /everything-claude-code:docs |
| Frontend patterns | /everything-claude-code:frontend-patterns |
| API design | /everything-claude-code:api-design |
| E2E testing | /everything-claude-code:e2e-testing |
User Request (SSR)
→ Vinxi/TanStack Start server
→ TanStack Router (route match)
→ Route shell (createFileRoute)
→ TuvixApp (SSR bridge)
→ micro-apps/{name}/App.tsx
→ components/ (sub-components)
→ src/hooks/ (TanStack Query)
→ External APIs (GitHub / Dev.to / Medium / YouTube)
→ HTML rendered → sent to client → hydrated
Cross-app communication:
micro-app A → eventBus.emit('event:name', payload)
micro-app B → eventBus.on('event:name', handler)
any[]in useGithubRepos.ts —allRepos: any[]should be typedconsole.login useGithubRepos.ts — remove before production- Tailwind in app.css —
@tailwinddirectives remain until migration complete - tuvix.js local copy — source at
./tuvix.js/; use npm package in app - Turkish route names —
projeler,hakkimda,iletisimare intentional - BlogApp.tsx — standalone SPA version; listens to
tuvix:pathchangeevent - src/components/Home/ — legacy, to be deleted after micro-app migration
| Area | Status |
|---|---|
| Route thin shells | IN PROGRESS |
| home micro-apps (hero, about-preview, etc.) | TODO |
| about micro-app with sub-components | TODO |
| github / youtube micro-apps | TODO |
| projects micro-app | TODO |
| blog-list / post-detail micro-apps | TODO |
| contact micro-app (Vue) | TODO |
| footer micro-app (Svelte) | TODO |
| blogs-preview (Svelte) | TODO |
| contact-preview (Vue) | TODO |
| Event bus typed contract | TODO |
| @tuvix.js/svelte adapter | VERIFY |
| @tuvix.js/vue adapter | VERIFY |
| Tailwind removal | IN PROGRESS |
| CSS Modules adoption | IN PROGRESS |
| Lighthouse CI guard | TODO |
| Vitest setup | TODO |
DO NOT include git add, commit, or push commands. Only provide the commit message. The commit message must not include any "Co-author" information.