# portfolio Personal portfolio site. Next.js App Router frontend, PocketBase as the CMS, rendered with on-demand ISR — content is served static and revalidated by webhook when the CMS changes. ## Stack - **Next.js 16** (App Router, React 19, standalone output) - **TypeScript** (strict) - **Tailwind CSS v4** - **PocketBase** — headless CMS, fetched at build/runtime via a typed client - **Biome** — lint + format - **Vitest** + Testing Library — unit/component tests - **Storybook** (a11y + Vitest addons) — component workshop - **Yarn 4** Architecture follows [Feature-Sliced Design](https://feature-sliced.design/) adapted to Next.js (`src/{shared,entities,widgets}` + `app/`). ## Rendering Pages are statically generated and served through ISR. There is no fixed TTL — PocketBase calls the `/api/revalidate` webhook after any content mutation, which purges the matching cache tag (`revalidateTag`). See [`app/api/revalidate/route.ts`](app/api/revalidate/route.ts). ## Getting started ```bash yarn install yarn dev ``` Open http://localhost:3000. Without a reachable PocketBase, the app falls back to fixtures in [`app/api/collections/[collection]/records/route.ts`](app/api/collections/%5Bcollection%5D/records/route.ts). ## Environment ``` PB_URL # internal PocketBase URL (server-side fetches) PB_PUBLIC_URL # public PocketBase URL (image optimizer allowlist, build-time) REVALIDATE_SECRET # shared secret for the /api/revalidate webhook ``` `PB_PUBLIC_URL` must be present at `next build` time — `images.remotePatterns` is frozen into the build. ## Scripts | Command | Description | |---|---| | `yarn dev` | Dev server | | `yarn build` | Production build (standalone) | | `yarn start` | Serve the build | | `yarn test` | Run tests (Vitest) | | `yarn check` | Lint + format (Biome, writes fixes) | | `yarn check:ci` | Lint + format check (no writes) | | `yarn storybook` | Storybook on :6006 | ## Deployment Built as a standalone Docker image; PocketBase reached over the internal Docker network. Content updates trigger the revalidation webhook — no rebuild needed.